Signatur
Beschreibung
Die schreibgeschützte Eigenschaft firstUIEventTimestamp gehört zur Long Animation Frames API (LoAF). Sie gibt den Zeitpunkt zurück, zu dem das erste UI-Ereignis – etwa ein click, keydown oder pointerdown – im laufenden Frame in die Warteschlange kam. Gemessen wird relativ zu performance.timeOrigin, also auf derselben Zeitachse wie startTime und performance.now().
Nützlich ist der Wert vor allem, um Eingabeverzögerungen zu untersuchen: Liegt firstUIEventTimestamp deutlich vor dem Zeitpunkt, an dem der Frame mit dem Rendern fertig war (startTime + duration), dann hat die Benutzereingabe lange warten müssen. So lassen sich schlechte Werte der Metrik Interaction to Next Paint (INP) einem bestimmten langen Frame und damit bestimmten Skripten (scripts) zuordnen.
Gab es in dem Frame kein UI-Ereignis, ist der Wert 0. Einträge erhältst du über einen PerformanceObserver mit dem Typ "long-animation-frame". Als lang gilt ein Frame, wenn er länger als 50 ms dauert.
Rückgabewert
performance.timeOrigin, zu dem das erste UI-Ereignis im Frame in die Warteschlange kam. 0, wenn es in diesem Frame kein UI-Ereignis gab.Beispiele
Lange Frames mit Benutzereingabe protokollieren
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// Frames ohne UI-Ereignis überspringen
if (entry.firstUIEventTimestamp === 0) continue;
console.log(
`Langer Frame: ${entry.duration.toFixed(1)} ms, ` +
`erstes UI-Ereignis bei ${entry.firstUIEventTimestamp.toFixed(1)} ms`
);
}
});
observer.observe({ type: 'long-animation-frame', buffered: true });
Wartezeit der Eingabe bis zum Rendern berechnen
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
const { firstUIEventTimestamp, startTime, duration } = entry;
if (!firstUIEventTimestamp) continue;
const frameEnde = startTime + duration;
const wartezeit = frameEnde - firstUIEventTimestamp;
if (wartezeit > 100) {
console.warn(`Eingabe wartete ${Math.round(wartezeit)} ms auf das Rendern`);
// Verursachende Skripte ausgeben
for (const script of entry.scripts) {
console.warn(' ->', script.sourceURL, Math.round(script.duration), 'ms');
}
}
}
});
observer.observe({ type: 'long-animation-frame', buffered: true });
Feature-Erkennung und Versand an ein Analyse-Backend
const unterstuetzt = PerformanceObserver.supportedEntryTypes?.includes('long-animation-frame');
if (unterstuetzt) {
new PerformanceObserver((list) => {
const daten = list.getEntries()
.filter((e) => e.firstUIEventTimestamp > 0)
.map((e) => ({
start: Math.round(e.startTime),
dauer: Math.round(e.duration),
ersterUIEvent: Math.round(e.firstUIEventTimestamp),
blockiert: Math.round(e.blockingDuration)
}));
if (daten.length) {
navigator.sendBeacon('/api/loaf', JSON.stringify(daten));
}
}).observe({ type: 'long-animation-frame', buffered: true });
} else {
console.log('Long Animation Frames API nicht verfügbar');
}
// Wichtig · Fallstricke
- Browser-Unterstützung: Nur Chromium-Browser ab Version 123. Firefox und Safari unterstützen die API nicht. Prüfe vorher mit
PerformanceObserver.supportedEntryTypes, ob sie verfügbar ist. - Der Wert
0heißt kein UI-Ereignis und ist kein gültiger Zeitstempel. Filtere solche Einträge aus, bevor du rechnest. - Erfasst wird nur das erste UI-Ereignis im Frame. Weitere Eingaben im selben Frame tauchen nicht einzeln auf. Einzelne Interaktionen misst du genauer mit
PerformanceEventTiming(Typ"event"). - Der Zeitstempel zeigt, wann das Ereignis in die Warteschlange kam, nicht wann sein Handler lief.
- Zeitstempel können aus Datenschutzgründen (Schutz vor Timing-Angriffen) gerundet sein.