Start · Sprachen · JavaScript · Referenz · LayoutShiftAttribution

LayoutShiftAttribution

Schnittstelle

Beschreibt ein einzelnes DOM-Element, das sich bei einem Layout-Shift verschoben hat, samt Position und Größe vor und nach der Verschiebung.

seit JavaScript Chrome/Edge 84+, Opera 70+ (nur Chromium; nicht in Kategorie: webapi

Signatur

interface LayoutShiftAttribution

Beschreibung

LayoutShiftAttribution gehört zur Layout Instability API und liefert Debugging-Informationen darüber, welche Elemente eine Layout-Verschiebung verursacht haben. Instanzen erzeugt man nicht selbst: Man erhält sie über die Eigenschaft sources eines LayoutShift-Eintrags, den ein PerformanceObserver mit dem Eintragstyp "layout-shift" meldet.

Jede Attribution hat drei nur lesbare Eigenschaften und eine Methode:

  • node: das verschobene Element (Node) oder null, wenn es inzwischen aus dem DOM entfernt wurde.
  • previousRect: ein DOMRectReadOnly mit Position und Größe vor der Verschiebung (Viewport-Koordinaten).
  • currentRect: ein DOMRectReadOnly mit Position und Größe nach der Verschiebung.
  • toJSON(): liefert eine JSON-taugliche Darstellung der Attribution.

Die API ist vor allem nützlich, um den Core-Web-Vitals-Wert Cumulative Layout Shift (CLS) zu verbessern: Statt nur einen Zahlenwert zu kennen, sieht man, welche Bilder, Werbebanner, Webfonts oder nachgeladenen Inhalte die Seite springen lassen. Pro LayoutShift-Eintrag werden höchstens fünf Quellen gemeldet, sortiert nach ihrem Einfluss auf die Verschiebung.

Parameter

Name Typ Default Beschreibung
$node Node | null Nur lesende Eigenschaft: das verschobene Element oder null, falls es nicht mehr im DOM ist.
$previousRect DOMRectReadOnly Nur lesende Eigenschaft: Rechteck des Elements vor der Verschiebung. Leer (alle Werte 0), wenn das Element neu erschienen ist.
$currentRect DOMRectReadOnly Nur lesende Eigenschaft: Rechteck des Elements nach der Verschiebung. Leer, wenn das Element verschwunden ist.
$toJSON() function Methode: gibt ein einfaches Objekt mit node, previousRect und currentRect zurück.

Rückgabewert

Typ
LayoutShiftAttribution
Beschreibung
Keine Konstruktion möglich; Instanzen stehen im Array LayoutShift.sources bereit.

Beispiele

Verschobene Elemente in der Konsole auflisten

if (PerformanceObserver.supportedEntryTypes?.includes('layout-shift')) {
  const observer = new PerformanceObserver((list) => {
    for (const entry of list.getEntries()) {
      // Verschiebungen direkt nach Nutzereingaben zählen nicht zum CLS
      if (entry.hadRecentInput) continue;

      for (const source of entry.sources) {
        console.log('Verschoben:', source.node,
          'von y =', source.previousRect.y,
          'nach y =', source.currentRect.y);
      }
    }
  });
  observer.observe({ type: 'layout-shift', buffered: true });
} else {
  console.log('Layout Instability API wird nicht unterstützt.');
}
Verschoben: <div class="teaser"> von y = 320 nach y = 570

Verursacher visuell hervorheben (Debugging)

const highlight = (el) => {
  if (!(el instanceof HTMLElement)) return; // node kann null oder Textknoten sein
  el.style.outline = '3px solid red';
  setTimeout(() => { el.style.outline = ''; }, 2000);
};

new PerformanceObserver((list) => {
  list.getEntries()
    .filter((entry) => !entry.hadRecentInput)
    .flatMap((entry) => entry.sources)
    .forEach((source) => highlight(source.node));
}).observe({ type: 'layout-shift', buffered: true });

Größte Verschiebung per sendBeacon an Analytics melden

const describe = (node) => {
  if (!node || node.nodeType !== Node.ELEMENT_NODE) return '(entfernt)';
  const id = node.id ? `#${node.id}` : '';
  const cls = node.classList.length ? `.${[...node.classList].join('.')}` : '';
  return `${node.tagName.toLowerCase()}${id}${cls}`;
};

let worst = null;

new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    if (!entry.hadRecentInput && (!worst || entry.value > worst.value)) {
      worst = entry;
    }
  }
}).observe({ type: 'layout-shift', buffered: true });

document.addEventListener('visibilitychange', () => {
  if (document.visibilityState !== 'hidden' || !worst) return;
  const payload = {
    value: worst.value,
    sources: worst.sources.map((s) => ({
      element: describe(s.node),
      before: s.previousRect.toJSON(),
      after: s.currentRect.toJSON(),
    })),
  };
  navigator.sendBeacon('/analytics/cls', JSON.stringify(payload));
});

// Wichtig · Fallstricke

  • Browser-Kompatibilität: Nur in Chromium-Browsern (Chrome, Edge, Opera) verfügbar. Firefox und Safari unterstützen die Layout Instability API nicht. Deshalb immer vorher mit PerformanceObserver.supportedEntryTypes.includes('layout-shift') prüfen.
  • node kann null sein, etwa wenn das Element nach der Verschiebung entfernt wurde. Es kann auch ein Textknoten statt eines Elements sein.
  • Maximal fünf Quellen pro Eintrag: Bei großen Umbrüchen fehlen weniger bedeutende Elemente.
  • Die Rechtecke sind Viewport-Koordinaten zum Zeitpunkt der Verschiebung und berücksichtigen nur den sichtbaren Teil des Elements. Für Seitenkoordinaten muss man scrollX/scrollY selbst addieren.
  • toJSON() der Attribution serialisiert node nicht sinnvoll. Für Analytics besser einen eigenen Selektor-String erzeugen, statt DOM-Knoten zu speichern (sonst drohen auch Speicherlecks).
  • Einträge mit hadRecentInput === true gelten als erwartete Verschiebungen und zählen nicht zum CLS.