Layout Thrashing(強制同期レイアウト/Forced Reflow)とは、JavaScript が DOM への書き込みとレイアウト属性の読み取りを交互に繰り返すことで、ブラウザに同期的なレイアウト再計算を強制してしまう現象です。
レイアウト無効化(Layout Invalidation)のトリガー
先行する DOM 変更によってレイアウトが無効化されている状態で以下のプロパティ・メソッドを参照すると、ジオメトリの同期再計算が強制されます。
要素(Element)のプロパティ&メソッド
ボックスのジオメトリ・寸法
clientHeight,clientWidthclientLeft,clientTopoffsetHeight,offsetWidthoffsetLeft,offsetTopoffsetParentscrollHeight,scrollWidthscrollLeft,scrollTop
バウンディングボックスの取得
getBoundingClientRect()getClientRects()
スクロール操作
scrollIntoView()scrollIntoViewIfNeeded()scrollByLines()scrollByPages()
テキスト・フォーカス
innerTextouterTextfocus()(フォーカスリングおよびスクロール位置調整を強制)
マウスイベントプロパティ
MouseEvent インスタンスの特定座標を参照すると、一部ブラウザエンジンでレイアウト計算が発生します:
layerX,layerYoffsetX,offsetY
Window メソッド&プロパティ
window.getComputedStyle()(width、height、margin、transform などレイアウト依存の値を取得する場合)window.scrollBy()window.scrollTo()window.scrollX,window.scrollY
Frame・Document・Image
- レイアウト確定前に画像の
widthやheightを参照する操作 document.body.scrollHeightへのアクセス
jQuery 操作
jQuery の多くのヘルパーメソッドは内部で算出済みレイアウト値を読み取ります:
// リフロー(Reflow)を発生させる呼び出し
$(elem).offset();
$(elem).offsetParent();
$(elem).position();
$(elem).scrollLeft();
$(elem).scrollTop();
$(elem).css('width'); // レイアウト依存のスタイル計算すべて
$(elem).text();
// 幾何学的な可視性を検査する擬似セレクタ
$(':hidden');
$(':visible');
$(':contains');
Layout Thrashing を防ぐベストプラクティス
1. 読み取りと書き込みをバッチ処理する
必ずすべての DOM 計測を先に済ませ、その後まとめて DOM 更新を行います。
// NG: 読み取りと書き込みが交互になり毎回強制レイアウトが発生
elements.forEach((el) => {
const width = el.clientWidth; // 読み取り(レイアウト強制)
el.style.width = width * 2 + 'px'; // 書き込み(レイアウト無効化)
});
// OK: 読み取りをまとめてから、書き込みをまとめる
const widths = elements.map((el) => el.clientWidth); // 一括読み取り
elements.forEach((el, index) => {
el.style.width = widths[index] * 2 + 'px'; // 一括書き込み
});
2. requestAnimationFrame を活用する
スタイル変更を次のアニメーションフレームにスケジュールし、ブラウザの描画サイクルを最適化させます:
const height = element.clientHeight; // 現在のフェーズで読み取り
window.requestAnimationFrame(() => {
element.style.height = height + 50 + 'px'; // 次のフレームで書き込み
});
3. CSS Transform と Opacity を使う
アニメーションにはレイアウト系プロパティ(top、left、width、height)ではなくコンポジタ処理可能な CSS プロパティ(transform、opacity)を使いましょう。