Start · Sprachen · JavaScript · Referenz · LayoutShift

LayoutShift

Schnittstelle

Beschreibt einen einzelnen Layout-Shift, also eine unerwartete Verschiebung sichtbarer Elemente. Aus diesen Einträgen berechnet man die Kennzahl Cumulative Layout Shift (CLS).

seit JavaScript Chromium-Browser (Chrome/Edge 77+, Opera); nicht i Kategorie: webapi

Signatur

interface LayoutShift extends PerformanceEntry

Beschreibung

LayoutShift gehört zur Performance-API und steht für eine Layout-Verschiebung. Das passiert, wenn ein sichtbares Element zwischen zwei Frames seine Startposition ändert, zum Beispiel weil ein Bild ohne feste Maße nachlädt, ein Werbebanner eingefügt wird oder eine Web-Schrift die Textgröße verändert. Jeder Eintrag hat den entryType "layout-shift" und wird über einen PerformanceObserver gelesen.

Das Interface ist die Grundlage für die Core-Web-Vitals-Kennzahl Cumulative Layout Shift (CLS). Dafür werden die value-Werte aller Verschiebungen, die nicht kurz nach einer Nutzereingabe passiert sind, zu „Session-Fenstern“ zusammengefasst. Ein Fenster darf höchstens 5 Sekunden lang sein, und zwischen zwei Verschiebungen darf höchstens 1 Sekunde Pause liegen. CLS ist die Summe des größten dieser Fenster.

Wichtige Eigenschaften (zusätzlich zu denen von PerformanceEntry):

  • value (number): der Layout-Shift-Score, also Impact-Fraction × Distance-Fraction.
  • hadRecentInput (boolean): true, wenn in den letzten 500 ms eine Nutzereingabe stattfand. Solche Verschiebungen gelten als erwartet und zählen nicht zum CLS.
  • lastInputTime (DOMHighResTimeStamp): Zeitpunkt der letzten Eingabe.
  • sources (LayoutShiftAttribution[]): bis zu fünf Elemente, die sich am stärksten verschoben haben, jeweils mit node, previousRect und currentRect.
  • toJSON(): gibt eine JSON-Darstellung des Eintrags zurück.

Einsatzgebiete sind Real-User-Monitoring (RUM), die Fehlersuche bei „springenden“ Seiten und die Suche nach den Elementen, die Verschiebungen auslösen. Einen eigenen Konstruktor gibt es nicht: Instanzen erzeugt ausschließlich der Browser.

Rückgabewert

Typ
LayoutShift
Beschreibung
Instanzen bekommt man nur über PerformanceObserver (Typ "layout-shift"). performance.getEntriesByType() liefert für diesen Typ keine Einträge.

Beispiele

Layout-Shifts mit PerformanceObserver beobachten

const observer = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    console.log('Layout-Shift:', entry.value.toFixed(4),
      'nach Eingabe:', entry.hadRecentInput,
      'bei', Math.round(entry.startTime), 'ms');
  }
});

// buffered: true liefert auch Verschiebungen, die vor der Registrierung passiert sind
observer.observe({ type: 'layout-shift', buffered: true });
Layout-Shift: 0.0873 nach Eingabe: false bei 1240 ms

CLS mit Session-Fenstern berechnen

let cls = 0;
let sessionValue = 0;
let sessionEntries = [];

const po = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    // Verschiebungen nach Nutzereingaben ignorieren
    if (entry.hadRecentInput) continue;

    const first = sessionEntries[0];
    const last = sessionEntries.at(-1);

    // Neues Fenster, wenn Pause > 1 s oder Fenster länger als 5 s
    if (sessionEntries.length &&
        (entry.startTime - last.startTime > 1000 ||
         entry.startTime - first.startTime > 5000)) {
      sessionValue = 0;
      sessionEntries = [];
    }

    sessionValue += entry.value;
    sessionEntries.push(entry);
    cls = Math.max(cls, sessionValue);
  }
});

po.observe({ type: 'layout-shift', buffered: true });

// Beim Verlassen der Seite melden
document.addEventListener('visibilitychange', () => {
  if (document.visibilityState === 'hidden') {
    navigator.sendBeacon('/analytics', JSON.stringify({ cls }));
  }
});

Auslösende Elemente über sources finden

new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    if (entry.hadRecentInput || entry.value < 0.01) continue;

    for (const source of entry.sources) {
      const { node, previousRect, currentRect } = source;
      console.log('Verschoben:', node?.nodeName ?? '(entfernt)',
        `y ${previousRect.y} → ${currentRect.y}`);
    }
  }
}).observe({ type: 'layout-shift', buffered: true });
Verschoben: IMG y 120 → 380

Unterstützung prüfen

const supportsLayoutShift =
  PerformanceObserver.supportedEntryTypes?.includes('layout-shift') ?? false;

console.log('LayoutShift unterstützt:', supportsLayoutShift);
LayoutShift unterstützt: true

// Wichtig · Fallstricke

  • Browser-Unterstützung: Nur Chromium-Browser (Chrome, Edge, Opera, Samsung Internet). Firefox und Safari unterstützen das Interface nicht. Prüfen Sie die Unterstützung vorher mit PerformanceObserver.supportedEntryTypes. In Firefox und Safari liefert CLS-Monitoring deshalb keine Daten.
  • Zahlt nicht direkt in CLS ein: Einträge mit hadRecentInput === true müssen Sie selbst herausfiltern. Die bloße Summe aller value-Werte entspricht nicht der offiziellen CLS-Definition mit Session-Fenstern. Für produktive Messungen empfiehlt sich die Bibliothek web-vitals.
  • Nicht über getEntries abrufbar: performance.getEntriesByType('layout-shift') liefert ein leeres Array. Verwenden Sie PerformanceObserver mit buffered: true.
  • sources ist begrenzt: Die Liste enthält höchstens fünf Einträge. node kann null sein, wenn das Element inzwischen aus dem DOM entfernt wurde.
  • Häufige Ursachen: Bilder und iframes ohne width/height bzw. aspect-ratio, nachträglich eingefügte Inhalte und Web-Fonts (FOUT). Animationen mit transform erzeugen keine Layout-Shifts.
  • Den CLS-Wert sollten Sie beim visibilitychange auf hidden melden, nicht bei unload. Das Ereignis unload ist unzuverlässig und verhindert den Back/Forward-Cache.