こんにちは!フロントエンドの現場を渡り歩いているチーフアーキテクトの私です。
Web制作やフロントエンドの勉強をしていると、必ずと言っていいほど出会うのが「要素を画面から消す方法」ですよね。CSSの `display: none;` と `visibility: hidden;` 。どちらも「要素を隠す」という同じ目的を持っているように見えるため、最初は「どっちを使っても一緒でしょ?」と思いがちです。私も駆け出しの頃は適当に使い分けて、あとから「うわっ、レイアウトが崩れた!」なんて冷や汗をかいたものです。
でも、ご安心ください。この2つの違いは、Webブラウザの「頭の中(レンダリングの仕組み)」を少しだけ覗いてみると、すごくスッキリ腑に落ちます。
今回は、ブラウザが裏側でどうやって画面を作っているのか、身近な例え話を交えながら優しく紐解いていきましょう!
—
ブラウザの仕事は「レストランのメニュー作り」に似ている?
まず、Webブラウザが画面を表示するまでの裏側のストーリーを少しだけお話しさせてください。私たちが書いたHTMLやCSSは、そのままではただの文字の羅列です。ブラウザはこれを受け取ると、次のような手順で料理(画面)を仕上げます。
1. HTMLを読む(パースする):「材料はトマトが3つ、お肉が100g……」と素材を確認します。
2. DOMツリーを作る:材料同士の関係性(親子関係)を整理し、レシピの全体像を作ります。
3. レンダーツリーを作る:「今回はどの材料を実際にテーブルに出すか」を選別し、見た目の設計図を作ります。
4. レイアウト(リフロー):お皿の上のどこに、どれくらいのスペースを空けて配置するかを計算します。
5. ペイント(描画):最後に色を塗り、画面にピクセルを描き出します。
この中で、今日の主役である `display: none;` と `visibility: hidden;` が「どの段階でサボる(あるいは働く)のか」が、決定的な違いを生むポイントになります。
—
`display: none;` は「存在そのものを無かったことにする」
まずは `display: none;` から見ていきましょう。
この子は、先ほどのステップの「3番目(レンダーツリーを作る段階)」で、もう完全に姿を消します。ブラウザの頭の中では、「あ、この要素は最初から宇宙に存在していなかったんだな」と処理されます。
身近な例え:お店の「臨時休業」
想像してみてください。あなたが街を歩いていて、お気に入りの洋服屋さんを見つけたとします。しかし、シャッターが閉まっていて「本日 臨時休業」の貼り紙が出ている。
そのお店の場所には、文字通り「そこにお店がない」かのように、前を通り過ぎるスペースがぽっかり空きますよね。誰も並ばないし、行列の邪魔にもなりません。
`display: none;` の特徴
- レンダーツリーに乗らない:ブラウザはレイアウトの計算時、この要素の存在を完全に無視します。
- リフロー(レイアウトの再計算)が起きる:このプロパティがオン・オフされると、周囲の要素が「あれ、急にスペースが空いたぞ(あるいは狭くなったぞ)」とドミノ倒しのように位置を再計算するため、ブラウザに少し負荷がかかります。
- イベントも起きない:マウスでクリックすることも、フォーカスを当てることもできません。完全に透明人間ならぬ「透明空気」です。
—
`visibility: hidden;` は「透明マントを被っているだけ」
一方で、`visibility: hidden;` はどうでしょうか。
こちらは、先ほどのステップでちゃんと「レンダーツリー」に参加します。ブラウザは「ここに要素があることは分かっているぞ、席もちゃんと確保してあるからな」と認識しています。ただ、最後の「ペイント(描画)」の段階で、透明インクを使って描くため、私たちの目には見えなくなっているだけなのです。
身近な例え:映画館の「キープされた座席」
大人気映画のチケットを買って、友達の分の席も隣にキープしておいたとします。友達がトイレに行っていてまだ席に戻ってきていないので、その座席には誰も座っていません(姿は見えません)。
でも、「そこに椅子(スペース)があること」は誰もが知っていますよね? だから、他の人が勝手にそこに座ることはできません。
`visibility: hidden;` の特徴
- レンダーツリーに乗る:要素の大きさや占有スペースはそのままキープされます。
- リフローが起きない:要素を隠したり再表示したりしても、周りのレイアウトがガタガタと動くことがありません。「スペースだけは死守する」という強い意志を感じます。
- 再描画(リパイント)だけで済む:レイアウトの計算をし直す必要がないため、ブラウザのパフォーマンスに優しいという特徴があります。
—
実際にコードで違いを体感してみよう!
百聞は一見にしかず。実際に手を動かして、その挙動の違いを確認してみましょう。
以下のコードをコピーして、ご自身の環境(メモ帳やVS Codeなど)でHTMLファイルとして保存し、ブラウザで開いてみてください。
ブラウザのレンダリング体験コーナー
【チーフからのヒント】
.target-none のコメントを外すと、箱3がスポッと上に移動しますよね(リフロー発生)。
逆に visibility: hidden; に切り替えると、箱2の場所がぽっかり空いたままになります。
ブラウザでこのファイルを開き、CSSを切り替えてみると、「あ、本当だ!スペースの残り方が全然違う!」と体感できるはずです。この感覚を掴んでおくと、あとでアコーディオンメニューやポップアップを作るときに迷わなくなりますよ。
—
現場でどう使い分けるべき?プロの判断基準
最後に、実務でどちらを選ぶべきかの判断基準をこっそりシェアしますね。
1. レイアウトを詰めて、完全に隠したいとき ➡️ `display: none;`
- スマホ用メニューの開閉や、タブ切り替えなど、「見えていない時は、そこに存在すらしていてほしくない」というケースのほとんどはこちらです。
2. 「形(レイアウト)は崩したくないけど、一時的に見えなくしたい」とき ➡️ `visibility: hidden;`
- 例えば、ローディングアニメーションのアイコンで、画像が読み込まれるまでの間だけ空間をキープしておきたい時や、グリッドレイアウトで「特定の場所だけ空っぽのボックスを置いて、綺麗に整列させ続けたい」というニッチな場面で活躍します。
ちなみに、アクセシビリティ(スクリーンリーダーなどの音声読み上げソフト)の観点からも、`display: none;` や `visibility: hidden;` が指定された要素は基本的に読み上げられません。もし「見た目は消したいけれど、画面いっぱいの文字情報を音声ユーザーにだけ伝えたい!」という場合は、これらのプロパティではなく、専用のCSSクラス(一般的に `sr-only` などと呼ばれます)を使って画面の外に飛ばす手法をとります。このあたりのお話は、また別の機会に深くお話ししましょう。
最初は少し難しく感じるブラウザの裏側の仕組みも、こうして「ブラウザの頭の中のストーリー」として想像してみると、グッと親しみやすくなったのではないでしょうか?
もしコードを書いていて「あれ、思った通りに動かないな」とつまずいても、大丈夫です。ベテランのエンジニアでも、レイアウトの崩れと格闘する日はたくさんあります。一つずつ、ブラウザの気持ちになりながら紐解いていけば必ず解決できますよ。
それでは、快適なフロントエンドライフを!

コメント