こんにちは!フロントエンドの現場を長年歩んできた者です。
Webサイトを作っていて、「なんだか最近、スマホでスクロールするとカクつくんだよなぁ……」「JavaScriptでアニメーションを動かしたら、妙に動作が重いな……」なんて悩んだ経験はありませんか?
私たちが普段何気なく書いているHTMLやCSS、そしてJavaScript。これらがブラウザという巨大な黒箱の中で、どのように料理され、画面に映し出されているのか。その裏側の仕組みを少しだけ覗いてみると、Web開発の景色がガラリと変わって見えてきます。
今回は、その心臓部の一つである「レイアウト(リフロー)」の仕組みについて、難しい専門用語はなるべく控えめに、身近な例え話とともにじっくりと紐解いていきましょう。
つまずきやすいポイントもたくさんありますが、「あぁ、そういうことね!」と腑に落ちる瞬間を用意したので、どうぞ肩の力を抜いてリラックスして読んでいってくださいね。
—
1. ブラウザが画面を表示するまでの「お引越し」のたとえ
まず、ブラウザがHTMLやCSSを読み込んで画面に表示するまでの大まかな流れをイメージしてみましょう。これは例えるなら、「新しい家(Webページ)に家具を運び込んで、レイアウトを決める大がかりなお引越し」のようなものです。
1. HTMLの解析(DOMツリーの構築):
段ボール箱に「本棚」「ベッド」「テーブル」といった荷物がどんどん詰め込まれていき、それぞれの関係性(どれがどこに入っているか)のリストが作られます。
2. CSSの解析(CSSOMツリーの構築):
「本棚は茶色、高さは2メートル」「ベッドは窓際に置く」といった、インテリアのコーディネート仕様書が作られます。
3. レンダーツリーの結合:
荷物のリストと、インテリアの仕様書を合体させます。「画面に実際に登場する家具とその見た目」の最終リストがここで完成します。
4. レイアウト(リフロー): ← 今回の主役!
部屋の図面を広げ、「このベッドは部屋の北西の、この座標に置こう」「この本棚の横幅は何センチだから、隙間に収まるな」と、正確な位置とサイズを計算する作業です。
5. ペイント & コンポジット(描画・合成):
実際にペンキを塗って色をつけ、透明なフィルム(レイヤー)ごとに重ね合わせて、最終的に私たちの目に見える画面として映し出します。
このお引越しのフェーズ4にあたる「レイアウト」こそが、今回深掘りするリフローの正体です。
—
2. 「リフロー」とは何か? なぜ起きるのか?
リフロー(Reflow)とは、一言でいうと「要素のサイズや位置を再計算するプロセス」のことです。
先ほどのお引越しの例でいえば、部屋の真ん中に置いた巨大なソファのサイズを後から「やっぱり幅2倍のものに変えよう!」と変えたと想像してください。どうなるでしょうか?
ソファが大きくなったせいで、隣にあったテーブルが押し出され、そのテーブルがさらに別の椅子を押し出し……と、たった一つの家具のサイズを変えただけで、部屋全体の配置をもう一度計算し直さなければならなくなりますよね。
Webブラウザの世界でもこれと全く同じことが起きています。
リフローが発生する主な条件
私たちが普段書くコードの中で、以下のような変更を加えると、ブラウザは「おっと、全体のサイズと位置を計算し直さなきゃ!」と慌ててリフローを始めます。
- ブラウザのウィンドウのサイズ(幅や高さ)を変えたとき(レスポンシブデザインの醍醐味ですね)
- JavaScriptを使って、要素の幅(`width`)や高さ(`height`)、余白(`margin`, `padding`)などを動的に変えたとき
- フォントの大きさを変えたとき
- 新しい要素をHTMLにJavaScriptで追加したり、削除したりしたとき
- 要素の位置(`top`, `left`など)を動かしたとき
……こうして見ると、Webサイトが動的であればあるほど、リフローは避けて通れない宿命なのだということが分かります。
—
3. ちょっと待って!「リフロー」の何が問題なの?
「ブラウザが勝手に計算し直してくれるなら、お任せしておけばいいじゃない」と思われるかもしれません。もちろん、初回の読み込み時に一度計算するのは当たり前の仕事です。
問題なのは、「ユーザーがスクロールしている最中や、アニメーションが動いている最中に、何回も何回もリフローが起きてしまうこと」です。
ブラウザは真面目なので、私たちがJavaScriptなどで「幅を1px広げて、高さを1px縮めて……」と細かく指示すると、その都度「はい!全体の位置を再計算します!」と部屋中の家具を動かし直します。これが、いわゆる「画面のカクつき(パフォーマンスの低下)」の大きな原因になります。
特にスマートフォンのような、パソコンに比べてパワーが限られたデバイスでは、このリフローの連続発生は致命傷になり得ます。
「でも大丈夫ですよ!」
すべてのリフローを悪者扱いする必要はありません。要は、「ブラウザに無駄な計算をさせないコツ」を知っていればいいのです。
—
4. 実務で役立つ!リフローを最小限に抑えるスマートな書き方
ここで、実際に私たちがエディタに向かうときのヒントを見てみましょう。
例えば、JavaScriptを使ってDOM要素のスタイルをゴリゴリと変更するシーンを想像してください。
❌ やってしまいがちな「リフローの嵐」を引き起こすコード
// よくやりがちな例:スタイルを1つ変えるたびにブラウザが計算し直してしまう
const box = document.getElementById(‘my-box’);
box.style.width = ‘200px’; // ← ここでリフロー発生!
box.style.height = ‘200px’; // ← ここでもリフロー発生!
box.style.margin = ’20px’; // ← ここでもリフロー発生!
これでは、ブラウザの作業員が「家具を動かす→ため息をつく→また家具を動かす→ため息をつく」の繰り返しで、すっかり疲れ果ててしまいます。
⭕ ブラウザに優しいスマートなコード
これを解決する方法はいくつかありますが、代表的なアプローチを2つご紹介します。
① `className` や `cssText` でまとめて変更する
ブラウザに「これから一気に変えるから、計算は最後にまとめて1回だけやってね!」と伝えるイメージです。
const box = document.getElementById(‘my-box’);
// クラスをごそっと付け替えることで、リフローの発生を「1回」に抑える
box.className = ‘is-expanded’;
これなら、ブラウザの作業員は一度だけ図面を引き直せば済むので、動作が圧倒的に軽くなります。
② 位置の「読み取り」と「書き込み」を交互にしない(強制同期レイアウトに注意)
さらにディープな話をすると、JavaScriptで要素のサイズを「読み取った直後」に、別の要素のサイズを「書き換える」というコードを交互に書くと、ブラウザは正確な値を返すために強制的にその場でリフローを起こします(専門用語で「レイアウトスラッシング」と呼ばれます)。
// ⚠️ 避けたほうがいい書き方(読み取りと書き込みが交互に起きる)
const currentWidth = box.offsetWidth; // 読み取り(ここで計算を強制される)
box.style.width = (currentWidth + 10) + ‘px’; // 書き込み
const currentHeight = box.offsetHeight; // 読み取り
box.style.height = (currentHeight + 10) + ‘px’; // 書き込み
これを防ぐためには、「最初に必要な寸法をまとめて全部読み取ってから、後でまとめて書き込む」というリズムを意識するだけで、パフォーマンスは見違えるほど向上します。
—
5. まとめ:ブラウザとお友達になるために
いかがでしたでしょうか?
今回は、Webブラウザの裏側で行われている「レイアウト(リフロー)」の仕組みについて、お引越しの例えを交えてお話ししました。
- リフローとは、要素の正確な位置やサイズを計算し直すプロセスのこと。
- 回数が多すぎると、画面がカクつく原因になる。
- スタイルはなるべくまとめて変更し、ブラウザへの負担を減らしてあげるのがフロントエンドエンジニアの優しい気配り。
Webブラウザは、私たちが書いたコードを忠実に、そして猛烈なスピードで形にしてくれる最高の相棒です。その裏側の仕組み(お引越しの苦労)を少しだけ想像してあげられるようになると、あなたの書くコードは驚くほど洗練されたものに変わっていきます。
ぜひ、日々のコーディングやJavaScriptの記述の際に、「いま、ブラウザに余計なお引越しをさせていないかな?」とちょっとだけ意識してみてくださいね。それでは、快適なフロントエンドライフを!

コメント