【入門編】 リフローを引き起こすプロパティと操作 – Webブラウザの仕組み実践ガイド

こんにちは!日々のコーディング、本当にお疲れ様です。

CSSをちょっと書き換えたら画面がガタッと動いた、JavaScriptで要素を追加したらなんだか動作がモッサリしてしまった……。そんな経験はありませんか?

「自分のコード、何が悪かったんだろう……」と落ち込む必要はまったくありません!それはあなた技術不足ではなく、Webブラウザという「超がつくほど生真面目で、少し心配性な職人さん」の裏側の仕組みをほんの少し知るだけで、綺麗さっぱり解決できる問題です。

今回は、ブラウザの描画プロセスの中でも一番の力仕事である「リフロー(レイアウトの再計算)」にスポットライトを当てます。
難しい専門用語をできるだけ使わず、お部屋の模様替えや日々のお買い物といった身近な例え話を使って、優しく、そして現場で役立つリアルな知見まで一気にお届けしますね。

準備はいいですか?ゆっくり、一緒に学んでいきましょう!

—

1. ブラウザの3大お仕事:リフロー、リペイント、コンポジット

私たちが書いたHTML、CSS、JavaScriptを画面に映し出すために、ブラウザは裏側で3つのステップを踏んで絵を描いています。
これを分かりやすく「お部屋の模様替え」に例えてみましょう。

| ブラウザの専門用語 | お部屋の模様替えで例えると? | 処理の重さ |
| :— | :— | :— |
| ① リフロー (Reflow) | 家具の配置決め(サイズ測り・レイアウト)
「この机は幅120cmだから、ここに置くと隣の棚が通れなくなるな…」と、メジャーを持って全員の位置を計算し直すこと。 | 激重(めちゃくちゃ大変!) |
| ② リペイント (Repaint) | 壁や家具のペンキ塗り(見た目・色の変更)
家具の位置はそのままに、引き出しの色を赤から青に塗り替えたり、壁紙を張り替えたりすること。 | 中くらい |
| ③ コンポジット合成 (Composite) | 透明なアクリル板を重ねる(レイヤーの合成)
「手前の飾り棚」と「奥の壁」を別々のアクリル板に描いておき、最後にスーッと重ね合わせて完成させること。 | 超軽量(一番ラクチン!) |

どうでしょう?
「リフロー」がいかに大変な作業かイメージできませんか?
部屋にある家具(要素)が1つでも大きくなったり、新しい家具がドスンと届いたりすると、ブラウザ職人は「ひえ〜!他の家具を全部ずらして、通路の幅を計算し直さなきゃ!」と、メジャーを持って部屋中を走り回る羽目になります。

これが、画面がカクついたり、重くなったりする正体なんです。

—

2. ブラウザ職人を大慌てさせる「リフローの引き金(トリガー)」たち

では、具体的にどんなコードを書くと、ブラウザ職人が「リフロー(大がかりな再計算)」を始めてしまうのでしょうか?
代表的な「引き金」を、4つのシチュエーションに分けて見てみましょう。

① 「家具が届いた!捨てた!」:DOMの追加・削除

新しく要素(`

`など)を作って画面に追加したり、逆に消したりする操作です。

  • 例: `document.body.appendChild(newElement)` や `element.remove()`
  • 職人の心の声: 「うわっ、新しい棚がリビングのど真ん中に届いたぞ!通路を確保するために、周りのソファーもテレビ台も全部ずらさなきゃ!」

② 「家具のサイズが変わった!」:サイズや位置、余白の変更

CSSで要素の大きさや、周りとの隙間を変更する操作です。

  • 該当するプロパティ: `width`, `height`, `padding`, `margin`, `border`, `top`, `left` など
  • 職人の心の声: 「テーブルの幅を広げるの!?じゃあ、隣に置いてあった観葉植物をちょっと右にずらして……あ、そうすると今度はドアにぶつかっちゃうから、ドアの前のマットもずらして……(無限ループ)」

③ 「文字の大きさが変わった!」:フォントやテキストの変更

文字のサイズ(`font-size`)や、フォントの種類(`font-family`)、あるいは中身の文章そのものを書き換える操作です。

  • 該当するプロパティ: `font-size`, `font-family`, `line-height`, `text-align` など
  • 職人の心の声: 「文字が大きくなったってことは、ここで改行しなきゃいけないな。改行したってことは、この文章全体の高さが2行分増えるぞ。ということは、この下にあるボタンも全員下にずり下がってもらわなきゃ!」

④ 【超重要】「今、ここ何センチ!?」:状態の読み取り

実は、これが一番の落とし穴です。
JavaScriptで「現在の要素の幅や高さを教えて!」とブラウザに質問するだけで、リフローが発生します。

  • 該当するプロパティ・メソッド:
  • `element.offsetWidth`, `element.offsetHeight`(要素の幅・高さ)
  • `element.offsetTop`, `element.offsetLeft`(要素の位置)
  • `window.scrollY`(スクロールした量)
  • `element.getBoundingClientRect()`(詳細な位置やサイズを測る魔法の呪文)

💡 なぜ「読み取るだけ」で重くなるの?(ブラウザのサボり癖)

実はブラウザ職人は、とても賢くて「サボり上手」です。
CSSの変更命令が何個も来ると、「どうせ後でまとめてやるから、今は計算しないでメモだけしておこう」と、作業を後回し(キューイング)にします。

しかし、その途中でJavaScriptから「ねえ、今のこのボタンの正確な幅(`offsetWidth`)を教えて!」と聞かれると、職人は大慌て!
「えっ!?今すぐ!?ちょっと待って、溜まってたメモを全部処理して、大急ぎで今現在のレイアウトを計算し直すから!」と、その場で強制的にリフローを実行します。

これを専門用語で「強制シンクロナスレイアウト(Forced Synchronous Layout)」と呼びます。

—

3. 【実践】ブラウザに優しいコード vs 怒らせるコード

では、実際のJavaScriptコードを見ながら、どうすればブラウザ職人に優しく(=パフォーマンス良く)コーディングできるかを学んでいきましょう!

今回は「画面に100個のリストアイテムを追加する」という処理を例にします。

❌ ブラウザ職人を過労死させる「悪い例」

まずは、ブラウザに何度もリフローを強制させてしまう、ちょっとかわいそうな書き方です。

// 画面にあるリストの親要素を取得します
const list = document.getElementById(‘my-list’);

// 100回ループを回して、要素を1つずつ追加していきます
for (let i = 0; i < 100; i++) { const item = document.createElement('li'); item.textContent = `アイテム ${i}`; // 【問題点①】1回ループするごとに、直接DOMに追加しています。 // ブラウザ職人は「あ、また家具が届いた!再計算!」「また届いた!再計算!」と、100回リフローを繰り返します。 list.appendChild(item); // 【問題点②】追加した直後に、高さを読み取っています。 // これにより、さっき後回しにしようとした計算を「今すぐやれ!」と強制(強制シンクロナスレイアウト)しています。 const height = item.offsetHeight; console.log(`アイテムの高さは ${height}px です`); }

⭕️ ブラウザ職人が思わず笑顔になる「良い例」

次に、職人に「さすが!気が利くね!」と言われる、優しくてスマートな書き方です。
ポイントは、「裏でまとめてから一気に渡す」ことと、「読み取りと書き込みを分ける」ことです。

const list = document.getElementById(‘my-list’);

// 1. 「仮の作業机(DocumentFragment)」を用意します。
// ここは画面の外(宇宙空間のような場所)なので、いくら要素をいじってもリフローは起きません。
const fragment = document.createDocumentFragment();

// 100個の要素を、まずはこの仮の作業机の上だけで組み立てます
for (let i = 0; i < 100; i++) { const item = document.createElement('li'); item.textContent = `アイテム ${i}`; // 仮の作業机に追加(ブラウザはまだ何もしなくていいので、無傷です) fragment.appendChild(item); } // 2. 最後に、完成した100個の塊を「せーの!」で1回だけ画面に追加します。 // これなら、ブラウザ職人がメジャーを持って走る(リフローする)のは「1回だけ」で済みます! list.appendChild(fragment); // 3. もしサイズを測りたいなら、すべての追加が終わった「後」で、まとめて1回だけ測りましょう。 // 読み書きが混ざらないので、無駄な再計算が発生しません。 const items = list.querySelectorAll('li'); items.forEach((item) => {
const height = item.offsetHeight; // 職人も「もう計算終わってるから、これだよ!」とすぐ答えてくれます
console.log(`アイテムの高さは ${height}px です`);
});

—

4. 今日からできる!ブラウザを労わる3つの約束

フロントエンドの開発において、リフローを「ゼロ」にすることはできません。画面を動かす以上、どうしても発生するものです。
でも、以下の3つの約束を心に留めておくだけで、あなたのサイトは劇的に軽くなりますよ。

1. 「DOMの変更は、裏でまとめて一気にドカン!」

  • `DocumentFragment` を使ったり、HTMLの文字列を1つの変数に合体させてから `innerHTML` で一気に流し込みましょう。

2. 「読み取る(Read)のと、書き換える(Write)のを交互にやらない!」

  • 「測って、変えて、また測って、また変えて……」はブラウザの天敵です。
  • 「まず全員分を測って(Read)、そのあと全員分を書き換える(Write)」 という順番を意識しましょう。

3. 「アニメーションは `transform` と `opacity` にお任せ!」

  • 要素を動かすとき、`left` や `top` をJSやCSSで細かく変えると、その都度リフローが発生します。
  • 代わりに `transform: translateX()` や `translateY()` を使いましょう。これらはなんと、最初にご紹介した「③ コンポジット合成(アクリル板を重ねるだけ)」の処理になるため、リフローが一切発生せず、スマホでもぬるぬる滑らかに動きます!

—

まとめ:ブラウザの気持ちに寄り添う、優しいエンジニアへ

最初は「なんだか難しそう……」と感じたかもしれません。でも、ブラウザを「生真面目な職人さん」だと思って眺めてみると、なんだか愛着が湧いてきませんか?

「ここにマージンを足したら、職人さんは周りの計算で大変かな?」
「じゃあ、今回は `transform` で動かしてあげよう!」

そんな風に、ブラウザの裏側の呼吸にそっと寄り添えるようになることが、一流のフロントエンド・スペシャリストへの第一歩です。

大丈夫、一気に全部を完璧にできなくても、この仕組みを「知っている」だけで、あなたの書くコードは明日から少しずつ優しくなっていきます。

これからも、焦らず楽しく、素敵なWebサイトを一緒に作っていきましょうね!応援しています!

コメント

タイトルとURLをコピーしました