Erst lesen, dann schreiben: Layout Thrashing vermeiden

Wer Lese- und Schreibzugriffe auf das DOM in einer Schleife mischt, zwingt den Browser, das Layout immer wieder neu zu berechnen. Besser bündeln.

Ändert man einen Style, berechnet der Browser das Layout nicht sofort neu. Er markiert es als veraltet und wartet bis zum nächsten Frame. Liest man danach aber einen Geometriewert wie offsetWidth, bleibt ihm keine Wahl: Er muss das Layout sofort neu berechnen, um eine korrekte Antwort zu liefern. Passiert das in einer Schleife, spricht man von Layout Thrashing.

Die langsame Variante

for (const el of items) {
  const width = el.parentNode.offsetWidth; // lesen: erzwingt Layout
  el.style.width = width / 2 + 'px';       // schreiben: macht Layout ungültig
}

Jeder Durchlauf macht das Layout ungültig, und der nächste erzwingt es erneut. Bei ein paar hundert Elementen kostet das schnell einige Dutzend Millisekunden.

Die schnelle Variante

const widths = items.map((el) => el.parentNode.offsetWidth); // alle Lesezugriffe
items.forEach((el, i) => {                                     // alle Schreibzugriffe
  el.style.width = widths[i] / 2 + 'px';
});

Gleiches Ergebnis, ein einziges Layout. Die Regel ist einfach: erst alles messen, was man braucht, dann ändern.

Was als Lesezugriff zählt

Die üblichen Verdächtigen sind Eigenschaften und Methoden, die Geometrie oder berechnete Werte liefern:

Erkennen

Im Performance-Panel der Entwicklertools zeigt sich Thrashing als viele kleine violette Layout-Blöcke innerhalb eines einzigen Tasks, oft mit einer Warnung zu Forced Reflow, die auf die verantwortliche Codezeile verweist. Muss der Code auf Größenänderungen reagieren, ist ein ResizeObserver meist besser, als selbst zu messen.

← Alle Notizen