Layout Thrashing (auch bekannt als erzwungenes synchrones Layout oder Forced Reflow) entsteht, wenn JavaScript wiederholt zwischen DOM-Schreibvorgängen und dem Auslesen von Layout-Eigenschaften wechselt, sodass der Browser das Seitenlayout synchron neu berechnen muss.
Auslöser der Layout-Invalidierung
Die folgenden DOM-Eigenschaften und Methoden erzwingen eine synchrone Neuberechnung der Geometrie, sobald das Layout zuvor durch DOM-Änderungen invalidiert wurde.
Element-Eigenschaften & Methoden
Box-Geometrie & Abmessungen
clientHeight,clientWidthclientLeft,clientTopoffsetHeight,offsetWidthoffsetLeft,offsetTopoffsetParentscrollHeight,scrollWidthscrollLeft,scrollTop
Bounding-Box-Abfragen
getBoundingClientRect()getClientRects()
Scroll-Aktionen
scrollIntoView()scrollIntoViewIfNeeded()scrollByLines()scrollByPages()
Text & Fokus
innerTextouterTextfocus()(erzwingt Fokusring- und Scroll-Anpassungen)
Maus-Event-Eigenschaften
Das Abfragen bestimmter Koordinaten eines MouseEvent kann in manchen Browser-Engines eine Layoutberechnung auslösen:
layerX,layerYoffsetX,offsetY
Window-Methoden & Eigenschaften
window.getComputedStyle()(beim Abfragen layoutabhängiger Werte wie Breite, Höhe, Margin oder Transform)window.scrollBy()window.scrollTo()window.scrollX,window.scrollY
Frame, Dokument & Bild
- Abfragen von
widthoderheighteines Bildes vor Stabilisierung des Layouts - Zugriff auf
document.body.scrollHeight
jQuery-Operationen
Viele jQuery-Hilfsmethoden lesen intern berechnete Layoutwerte aus:
// Löst Reflow aus
$(elem).offset();
$(elem).offsetParent();
$(elem).position();
$(elem).scrollLeft();
$(elem).scrollTop();
$(elem).css('width'); // Jede layoutabhängige Stilberechnung
$(elem).text();
// Pseudoselektoren, die geometrische Sichtbarkeit prüfen
$(':hidden');
$(':visible');
$(':contains');
Best Practices gegen Layout Thrashing
1. Lese- und Schreibvorgänge bündeln
Führen Sie stets alle DOM-Messungen zuerst aus und bündeln Sie anschließend alle DOM-Mutationen.
// SCHLECHT: Verschränktes Lesen und Schreiben erzwingt Layout je Iteration
elements.forEach((el) => {
const width = el.clientWidth; // LESEN (erzwingt Layout)
el.style.width = width * 2 + 'px'; // SCHREIBEN (invalidiert Layout)
});
// GUT: Lesevorgänge bündeln, danach Schreibvorgänge bündeln
const widths = elements.map((el) => el.clientWidth); // GEBÜNDELTES LESEN
elements.forEach((el, index) => {
el.style.width = widths[index] * 2 + 'px'; // GEBÜNDELTES SCHREIBEN
});
2. requestAnimationFrame nutzen
Planen Sie Stiländerungen im nächsten Animations-Frame ein, damit der Browser den Renderzyklus optimieren kann:
const height = element.clientHeight; // Lesen in der aktuellen Phase
window.requestAnimationFrame(() => {
element.style.height = height + 50 + 'px'; // Schreiben im nächsten Frame
});
3. CSS Transforms & Opacity verwenden
Animieren Sie bevorzugt CSS-Eigenschaften auf dem Compositor-Thread (transform, opacity) statt Layout-Eigenschaften (top, left, width, height).