やあ。今日も今日とてCSSとDOMの機嫌を取りながら、ブラウザの機密を暴く時間だな。
フロントエンドをある程度やってくると、「要素を隠す」という要件に対して `display: none` を使うべきか、それとも `visibility: hidden` を使うべきか、なんてのは脊髄反射で選べるようになるはずだ。
だがな、中級の壁をぶち破って「真のシニア」領域に到達するためには、「ブラウザが裏側のC++(BlinkやWebkit)の世界で、メモリとCPUの汗をかいてどう処理しているか」を解像度高くイメージできなきゃいけない。
今回は、この2つのプロパティが「レンダーツリーの構築」というレンダリングパイプラインの心臓部において、いかに非対称な運命を辿るのか、その裏側の仕組みを丸裸にしてやろう。
—
1. ブラウザの心臓部:レンダーツリー構築の仕組み
まず、ブラウザが画面を映し出すまでの大まかな旅路を思い出してくれ。
HTMLをパースして DOMツリー ができ、CSSをパースして CSSOMツリー ができる。そして、この2つをマキアージュ(融合)させて生み出されるのが レンダーツリー(Render Tree) だ。
ここがポイントだ。
DOMツリーは「HTML構造の完全な鏡」だが、レンダーツリーは「画面に描画されるべき実体だけの世界」なのだ。
画面にピクセルを描画する必要のない要素――例えば `
` タグや、これから話す `display: none` が適用された要素は、このレンダーツリーの構築フェーズにおいて、無慈悲にも存在を抹殺(除外)される。—
2. `display: none` と `visibility: hidden` の決定的な違い
さて、本題の2つを比較しよう。ブラウザの内部処理において、こいつらは全く異なるルートをたどる。
`display: none`:存在そのものの抹殺
- レンダーツリーへの影響: 完全に参加しない(ツリーから除外される)。
- メモリとレイアウト: ブラウザはその要素の存在を完全に無視する。当然、ジオメトリ(位置や大きさ)の計算対象外だ。
- 継承: 子孫要素も含めて一切描画されない。
`visibility: hidden`:透明人間のドレスコード
- レンダーツリーへの影響: 参加する(ツリーにノードが生成される)。
- メモリとレイアウト: 見た目は消える(透明になる、あるいは不透明度が0のピクセルとして扱われる)が、レイアウト上のスペースはそこにガッツリ確保され続ける。 ボックスモデルの計算において、ブラウザは相変わらずその要素の幅と高さを真面目に計算し続けているんだ。
- 継承: 子孫要素に `visibility: visible` を指定すれば、親が `hidden` であっても子だけを可視化するという変態的な芸業が可能。
—
3. リフローと再描画(Repaint)のメンタルモデル
実務でパフォーマンスを語る上で避けて通れないのが、リフロー(Layout / Reflow) と 再描画(Repaint) のコストだ。
- リフロー: 要素のサイズや位置が変わり、周囲のレイアウトを再計算する処理(CPU負荷:激高)
- 再描画: 色や透明度など、見た目だけが変わる処理(CPU負荷:中〜低)
ここで、それぞれのプロパティを「切り替えた瞬間」に何が起きるか整理しておこう。
| 操作 | `display: none ⇄ block` | `visibility: hidden ⇄ visible` |
| :— | :— | :— |
| 発生する処理 | リフロー + 再描画 | 再描画のみ(レイアウト再計算なし) |
| パフォーマンス | 周囲の巻き込みリフローで重いケースがある | 非常に軽量 |
「動的に要素の出し入れを頻繁に行うUI(例えばアコーディオンメニューやタブ切り替え)」において、安易に `visibility: hidden` を使うと、レイアウトの隙間が空いたままになってしまったり、逆に `display: none` を頻繁にトグルすると高頻度のリフローを引き起こしてフレームレート(60fps)が死ぬ原因になる。
このトレードオフを頭に叩き込んでおくんだ。
—
4. 実務で即座に使える実践コード例
百聞は一見にしかずだ。エディタを開いて、以下のコードを動かしてみな。
「レイアウトへの影響」と「アクセシビリティ(スクリーンリーダーへの配慮)」の違いを体験できるはずだ。
レンダリング挙動の比較
真ん中のボックスに注目してください。
—
シニアアーキテクトからのまとめ
ブラウザの仕組みを理解しているエンジニアと、そうでないエンジニアの決定的な違いは、「動くものを作った後になぜそれが動くのか、どこにコストがかかっているかを説明できるか」に尽きる。
- 構造ごと存在を消したい、あるいはパフォーマンスの観点から初回描画の計算コストを下げたい: `display: none`
- レイアウトのガタつき(レイアウトシフト / CLS)を防ぎたい、あるいはアニメーションの途中で一時的に消したい: `visibility: hidden`(または `opacity: 0`)
この使い分けを、単なる「お作法」ではなく、「ブラウザのレンダーツリー構築とリフローの仕組み」という物理法則ベースで語れるようになってくれ。
お前のコードのクオリティが一段階跳ね上がるはずだ。さて、次のタスクに取り掛かろうか。

コメント