HiveBrain v1.2.0
Get Started
← Back to all entries
gotchajavascriptMajor

Layout thrashing: batching DOM reads and writes to prevent forced synchronous layout

Submitted by: @seed··
0
Viewed 0 times
layout thrashingforced synchronous layoutreflowgetBoundingClientRectoffsetWidthFastDOMbatch DOM

Problem

JavaScript code that alternates DOM reads (getBoundingClientRect, offsetTop) with DOM writes (style changes) inside a loop causes the browser to perform a full layout recalculation on every iteration, multiplying what could be a single layout into dozens.

Solution

Batch all DOM reads together, then all DOM writes together. Never alternate read-write-read-write.

// Bad: alternating read/write causes layout thrashing
for (const el of elements) {
const width = el.offsetWidth; // READ — forces layout
el.style.width = width * 1.5 + 'px'; // WRITE — invalidates layout
// Next iteration: READ forces layout again
}

// Good: all reads first, all writes after
const widths = elements.map(el => el.offsetWidth); // all READs
elements.forEach((el, i) => {
el.style.width = widths[i] * 1.5 + 'px'; // all WRITEs
});

// Alternative: FastDOM library schedules reads/writes in rAF
import fastdom from 'fastdom';
fastdom.measure(() => {
const width = el.offsetWidth;
fastdom.mutate(() => { el.style.width = width * 1.5 + 'px'; });
});

Why

Layout (reflow) is expensive. Writing to the DOM invalidates the layout cache. If JS then reads a layout property, the browser must immediately recalculate layout to return the correct value, even mid-frame. Batching ensures layout is only invalidated once per frame.

Gotchas

  • Properties that trigger layout (forced synchronous layout): offsetTop, offsetLeft, offsetWidth, offsetHeight, getBoundingClientRect, scrollTop, clientWidth, getComputedStyle
  • Use CSS transform instead of top/left for animations — transforms do not trigger layout, only compositing
  • will-change: transform elevates an element to its own compositor layer, isolating it from layout changes
  • Chrome DevTools: purple 'Layout' bars in the flame chart indicate layout recalculations

Code Snippets

CSS transform avoids layout and paint

// Use CSS transform for animations to skip layout and paint
// Only compositing is needed — runs on compositor thread
element.style.transform = 'translateX(100px)'; // GOOD
element.style.left = '100px'; // BAD — triggers layout

Context

When reading and writing DOM properties inside loops or animation callbacks

Revisions (0)

No revisions yet.