The browser wants to batch style calculation and layout work. Performance problems show up when application code keeps interrupting that plan by alternating DOM writes and layout reads in tight loops.
That pattern is usually called layout thrashing.
The Bug Pattern
This is the expensive shape:
for (const item of items) {
item.style.width = "200px";
const rect = item.getBoundingClientRect();
item.style.height = `${rect.width}px`;
}
Every call to getBoundingClientRect() may force the browser to flush pending layout work before it can answer. If this runs during scroll, resize, or animation, the UI gets janky fast.
Better Pattern
Batch reads first. Batch writes second:
const widths = items.map((item) => item.getBoundingClientRect().width);
items.forEach((item, index) => {
item.style.width = "200px";
item.style.height = `${widths[index]}px`;
});
That is not the only possible fix, but it shows the underlying rule: avoid forcing the browser to repeatedly recompute layout inside frame-sensitive code.
When This Actually Matters
You usually feel it in:
- scroll handlers
- animation loops
- drag-and-drop interactions
- large lists with direct DOM manipulation
The browser is fast when you let it batch work. It becomes expensive when you make it answer geometry questions immediately after every write.
If the code has to run during animation or interaction, requestAnimationFrame is often the right place to schedule batched DOM work so reads and writes happen in a more predictable frame boundary.
Further Reading