Signatur
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) odernull, wenn es inzwischen aus dem DOM entfernt wurde.previousRect: einDOMRectReadOnlymit Position und Größe vor der Verschiebung (Viewport-Koordinaten).currentRect: einDOMRectReadOnlymit 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
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.');
}
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. nodekannnullsein, 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/scrollYselbst addieren. toJSON()der Attribution serialisiertnodenicht sinnvoll. Für Analytics besser einen eigenen Selektor-String erzeugen, statt DOM-Knoten zu speichern (sonst drohen auch Speicherlecks).- Einträge mit
hadRecentInput === truegelten als erwartete Verschiebungen und zählen nicht zum CLS.