強制同期レイアウト:DOM書き込み後にジオメトリを読む
何が起きているか
通常レイアウトはJavaScriptが終わった後、フレームごとに1回行われます。しかしDOMを変更して直後にレイアウトプロパティ — offsetWidth、scrollHeight、getBoundingClientRect()、getComputedStyle() — を読むと、答えがあなたの未反映の変更に依存するため、ブラウザはその場で、同期的に、タスク内でレイアウトを計算せざるを得ません。
強制レイアウトは小さなページでは安価です。大きなDOMや、ループで繰り返す(レイアウトスラッシングになる)と、ハンドラーに数百msを追加します。
見分け方
- INPの内訳でprocessing timeが長く、DevTools PerformanceでLayoutブロックがスクリプトタスク内に入れ子になっている(「Forced reflow」警告)。
- Long Animation Frames APIがあなたのスクリプトに大きなforcedStyleAndLayoutDurationを報告する。
- ハンドラーがDOMの変更と要素の計測を同時に行っている。
直し方
- タスク内で最初のDOM書き込みより前に、すべてのジオメトリを読む。
- 変わらない計測値(要素サイズ、コンテナ幅)はイベントごとに再読み込みせず、キャッシュする。
- 変更後にどうしても測るなら、読み取りを次フレームのrequestAnimationFrameに遅らせるか、ジオメトリをポーリングせずResizeObserver / IntersectionObserverを使う。
問題
grid.style.width = next + "px" // write: layout is now dirty
const rect = cell.getBoundingClientRect() // read → forced synchronous layout
修正
const rect = cell.getBoundingClientRect() // read first, layout is clean
grid.style.width = next + "px" // write after; browser lays out
// once, before the next paint
サイトを無料でスキャン →