Zruck zum Blog, du Zniachtl
Aug. 2026·4 Min. zum LesnVo da KI gmocht

Reflows und Repaints: Warum si deine Seitn ruckelig anfühlt

#performance#browser#frontend

Du misst die Höhe von an Element mit offsetHeight, nutzt sie, um die Breite von an ondern zu setzen, liest die Breite wieda aus und setzt die Höhe von an dritten - und irgendwie versenkt diese fuffzehn Zeiln lange Funktion ganz alloa deine Framerate. Der Schuldige taucht bei naivem Lesn vom Code fost nie ois "langsames JavaScript" auf. Es is der Browser, der still und leise s'komplette Layout deiner Seitn mehrmals neu berechnet, wegn da Reihenfolge, in der du die Sochn angefordert host.

Wos a Reflow wirklich is

A Reflow (a Layout gnannt) is, wenn da Browser die Geometrie der Seitn neu berechnet - wo jedes Element sitzt, wia groß's is, wia Text umbricht -, weil sich wos gändert hot, des Positionen oder Größen beeinflusst: a Klassenwechsel, der width ändert, a neiches Element im DOM, sogar a Schrift, die fertig downloadet. A Repaint is billiger: Die Geometrie is gleich blieben, aber Pixel miassn neu gezeichnet wern, weil si a Farbe oda a Schatten gändert hot. Reflows sand die teuren, weil a einzelne Layout-Änderung kaskadieren kann - a Box zum Resizen kann olles darunter und daneben verschiebn.

Browser wissn des, drum bündeln's Layout-Orbeit standardmäßig. Wennst fünf Style-Eigenschaften hintereinander änderst, berechnet da Browser s'Layout ned fünfmoi neu - er wartet bis zum nächsten Frame, mocht oan Durchgang und gengt weiter.

Die Falle: Layout-Eigenschaften lesn

Dieses Bündeln bricht zam, sobois du nach'm Schreiben a layoutabhängige Eigenschaft liest. Eigenschaften wia offsetHeight, offsetWidth, getBoundingClientRect() und scrollHeight kinnan nur korrekt beantwortet wern, wenn s'interne Layout vom Browser aktuell is - also erzwingt des Lesn von oaner a sofortige, synchrone Neuberechnung und wirft die Bündelungs-Optimierung weg.

const box = document.querySelector('.box');

box.style.width = '300px';       // write
const height = box.offsetHeight; // read — forces a synchronous reflow right here
box.style.height = height + 'px'; // write

A einzelnes Schreib-dann-Lese-Paar wia des is ned katastrophal. Der echte Schaden passiert, wenn dieses Muster in ana Schleifn läuft:

const items = document.querySelectorAll('.item');

items.forEach((item) => {
  item.style.width = '100px';         // write
  const height = item.offsetHeight;   // read — forces reflow, every iteration
  item.style.height = `${height * 2}px`;
});

Des nennt si "Layout Thrashing": schreiben, lesen, schreiben, lesen, wobei jedes Lesn den Browser zwingt, Layout-Orbeit nochamoi zu machen, die er grod erledigt hot, wieda und wieda, einmal pro Element. Hundert Elemente heißt hundert erzwungene synchrone Reflows für wos, des oans hätt sei soin.

Die Lösung: Erst alle Lesevorgänge bündeln, dann alle Schreibvorgänge

Die Lösung is fost mechanisch, sobois ma das Muster sieht: Trenn olle Lesevorgänge von allen Schreibvorgängen.

const items = document.querySelectorAll('.item');

// Read phase — collect everything first
const heights = Array.from(items).map((item) => item.offsetHeight);

// Write phase — apply everything after
items.forEach((item, i) => {
  item.style.width = '100px';
  item.style.height = `${heights[i] * 2}px`;
});

Jetzt gibt's genau oan Stapel an Lesevorgängen (der Browser kann sie olle aus dem Layout beantworten, das er scho hot, weil sich dazwischen nix gändert hot) und oan Stapel an Schreibvorgängen (die der Browser aufschieben und vor dem nächsten Paint zu oam einzigen Reflow zammfassen kann).

Schreibvorgänge mit requestAnimationFrame einplanen

Bei Layout-Änderungen, die von Scroll- oder Resize-Handlern ausglöst wern, wickel den Schreibvorgang in requestAnimationFrame ein, damit er direkt vor'm Paint-Zyklus vom Browser landet, statt mit ihm zu konkurrieren:

let ticking = false;

window.addEventListener('scroll', () => {
  if (ticking) return;
  ticking = true;

  requestAnimationFrame(() => {
    const scrollY = window.scrollY; // read
    header.style.transform = `translateY(${Math.min(scrollY, 100)}px)`; // write
    ticking = false;
  });
});

Das ticking-Flag verhindert, dass mehr ois a ausstehender Frame an Orbeit in die Queue kummt - ohne des kann a schnelles Scrollen Dutzende Callbacks einreihen, die olle hintereinander feuern.

Eigenschaften bevorzugn, die Layout komplett überspringen

Monche CSS-Eigenschaften kann ma animiern, ohne Layout oda sogar Paint auszulösn - der Browser kann sie direkt an den Compositor übergeben. transform und opacity sand die zwoa, die in der Praxis am meisten zähln:

/* Triggers layout on every frame — avoid animating this */
.slide-in {
  transition: left 0.3s ease;
}

/* Compositor-only — cheap regardless of how large the element is */
.slide-in {
  transition: transform 0.3s ease;
}
// Instead of animating `left`, `top`, `width`, or `height`...
el.style.left = '200px';

// ...animate transform, which doesn't touch layout at all
el.style.transform = 'translateX(200px)';

Bei an Element, des si bewegt oda ausblendet, is des meistens a direkter Austausch, und es is die wirkungsvollste Einzeländerung, die du an ana ruckligen Animation mochen kannst.

Thrashing finden, von dem du nix gwusst host

Das Performance-Panel der DevTools zeigt des direkt an - aufgezeichnete Reflows erscheinen ois lila "Layout"-Balken, und Chrome weist in der Konsole speziell auf "Forced reflow" hin, wenn a Skript oan auslöst, mit am Stacktrace, der auf die genaue Zeiln zeigt, die mittn im Batch a Layout-Eigenschaft glesn hot. Zeichn a poar Sekunden von der Interaktion auf, die si langsam anfühlt, und such nach am Sägezahn-Muster aus lila Balken; a flaches Stück Gelb (Skript) ohne Lila bis zum Frame-Ende heißt, du thrashst ned, du machst einfach a langsame Berechnung - a ondres Problem mit ana ondern Lösung.

Fazit

Da Browser bündelt Layout-Orbeit gern für dich - bis du mittn im Batch a Geometrie-Eigenschaft liest und ihn zwingst, früher aufzuholn. Trenn Lesen von Schreiben, schieb Schreibvorgänge nach requestAnimationFrame, wenn sie von kontinuierlichen Events getrieben wern, und greif zu transform/opacity statt left/width/height, wo immer der visuelle Effekt es erlaubt. Nix davon braucht a Framework oda a Bibliothek; es geht ausschließlich um die Reihenfolge, in der die Operationen passiern.

NeicherA einfache State Machine in TypeScript bauenElterHTTP-Caching-Header vastehn