← Concepts

阅读中文版本

Layout Thrashing

Layout thrashing is the read-write-read cycle where JavaScript reads layout geometry, mutates styles, and forces the browser to synchronously recompute layout dozens of times per frame.

browser

Definition

Layout thrashing (forced synchronous layout) occurs when code interleaves layout-reading properties like offsetHeight or getBoundingClientRect with style writes. Each read after a write forces layout computation synchronously before the frame, so the browser cannot batch, and cost multiplies to frame-budget overruns and jank.

Context and scope

Appears in drag-and-drop handlers, parallax scroll effects, virtualized list positioning, and any animation loop that queries geometry then writes styles per element in a loop.

Common misconceptions

  • Only heavy pages thrash — a simple list thrashes if reads and writes alternate per element in a loop
  • Reading layout is free — reads force layout when pending changes exist; the first read after a write pays full cost
  • Transforms cause layout — transform and opacity are compositor-friendly and skip layout entirely

Anti-patterns

  • Reading offsetTop inside the same loop iteration that writes element styles, forcing layout per element
  • Running layout-reading code in scroll or mousemove handlers at event frequency instead of frame frequency
  • Querying geometry for hundreds of nodes then writing one style each, interleaved rather than batched

Related concepts

Related examples

Authoritative source

https://developer.mozilla.org/en-US/docs/Glossary/Layout_thrashing

What it is

The browser wants to run layout once per frame, after all style writes. Interleaved reads defeat this: a read of layout-dependent geometry after pending writes forces the layout to run immediately and synchronously on the main thread. Loop that pattern over many elements and layout runs that many times.

How it works

Style writes mark the document dirty; the next layout query — offsetWidth, getBoundingClientRect, computed style of geometry-affecting properties — flushes the dirty state. The fix is phase separation: batch every read first into cached values, then apply all writes. rAF callbacks schedule the frame coherently.

When to fix

Long Animation Frames entries and forced-reflow warnings in performance traces pinpoint the offenders. Drag interactions, spreadsheet-like UIs, and chart animations are the usual hosts. Moving pure animation to transform and opacity offloads work to the compositor and sidesteps layout altogether.

Pitfalls

Hidden readers lurk in libraries: calling getBoundingClientRect inside a scroll listener per event can force layout more often than frames exist. Caching geometry between frames trades staleness for speed; recompute on resize through ResizeObserver instead of reading inline.