CSSで要素を非表示にする方法は?display・visibility・opacityの違いと使い分け
CSSで要素を非表示にする方法は、display・visibility・opacityの3つが基本です。結論から書くと、非表示のときに領域を詰めたいならdisplay: none、領域を残したいならvisibility: hidden、フェードさせたいならopacity: 0を選びます。
3つは「見えなくなる」という結果が同じなので混同されがちですが、レイアウトへの影響・アニメーションの可否・キーボード操作やスクリーンリーダーからの見え方がそれぞれ違います。私はLP制作で毎回この使い分けを判断しているので、その基準を表と実際のコードで整理しました。
記事の後半には、4パターンの動作を確認できるCodePenのデモも置いています。
display・visibility・opacityの違いは?一覧で比較
3つのプロパティは、「非表示のときに領域が残るか」と「その要素を操作できてしまうか」で決定的に違います。見た目が消える点だけが共通していて、それ以外はほぼ別物です。
▼3つのプロパティの違い
| 項目 | display: none |
visibility: hidden |
opacity: 0 |
|---|---|---|---|
| 非表示時の領域 | 詰まる | 残る | 残る |
transition |
△(後述の指定が必要) | △(0か1で切り替わる) | ◯(滑らかに変化する) |
| クリックできるか | できない | できない | できてしまう |
| Tabキーで移動できるか | できない | できない | できてしまう |
| スクリーンリーダーが読むか | 読まない | 読まない | 読み上げる |
| 子要素だけ再表示 | できない | できる(visibility: visible) |
できない |
この表でいちばん注意したいのがopacity: 0の行です。opacity: 0は見た目を透明にするだけなので、要素そのものはページに残り続けます。閉じたはずのメニューがTabキーで選べたり、スクリーンリーダーに読み上げられたりするのはこれが原因です。
領域を詰めたいなら display: none
非表示のときにその要素の場所を空けたくないならdisplay: noneを使います。要素はレイアウト計算から完全に外れ、後ろの要素が上に詰まります。
▼display: none の書き方
.box {
display: block;
}
.box.is-hidden {
display: none;
}
display: none はアニメーションが効かないのか
かつてはdisplayにtransitionは一切効きませんでした。ただし現在は、transition-behavior: allow-discreteと@starting-styleを組み合わせることで、display: noneへの切り替えにもアニメーションを付けられます。
▼display をアニメーションさせる書き方
.box {
display: block;
opacity: 1;
transition: opacity 0.3s, display 0.3s;
transition-behavior: allow-discrete;
}
.box.is-hidden {
display: none;
opacity: 0;
}
/* 表示され始めるときの開始値 */
@starting-style {
.box {
opacity: 0;
}
}
対応状況は、@starting-styleがChrome 117以降・Edge 117以降・Safari 17.5以降・Firefox 129以降、transition-behaviorがChrome 121以降・Safari 17.5以降・Firefox 129以降です。非対応のブラウザではアニメーションが付かないだけで、表示・非表示の切り替え自体は正しく動くため、そのまま書いて問題ありません。
display: none を使うと良い場面
折りたたんだ中身を完全に畳みたいときに向いています。アコーディオンやハンバーガーメニューの中身はdisplay: noneが基本です。領域が残ると、閉じているのに下に大きな空白ができてしまうためです。
実装の具体例はCSSとjQueryでアコーディオンを実装するで書いています。
領域を残したまま消したいなら visibility: hidden
レイアウトを崩さずに要素だけ消したいならvisibility: hiddenを使います。要素の領域はそのまま確保され、周りの要素は動きません。
▼visibility: hidden の書き方
.box {
visibility: visible;
}
.box.is-hidden {
visibility: hidden;
}
visibility: hidden は子要素だけ表示に戻せる
visibilityは継承されるプロパティなので、親をhiddenにしたまま、子要素にだけvisibility: visibleを指定して表示に戻せます。これはdisplayやopacityにはできない挙動です。
.parent {
visibility: hidden;
}
.parent .child {
visibility: visible; /* 親が hidden でも表示される */
}
visibility: hidden を使うと良い場面
表の行やグリッドの升目のように、消しても場所を空けてほしくない要素に向いています。キーボード操作とスクリーンリーダーからも外れるため、閉じているメニューを隠す用途でも安全です。
フェードで消したいなら opacity: 0
滑らかに消したいならopacity: 0を使います。3つの中で唯一、transitionで中間の状態を持てるプロパティです。
▼opacity でフェードさせる書き方
.box {
opacity: 1;
transition: opacity 0.3s ease;
}
.box.is-hidden {
opacity: 0;
}
opacity: 0 だけだとクリックできてしまう
opacity: 0の要素は透明になっただけで、そこに存在し続けます。透明なまま重なった要素が、下にあるボタンのクリックを奪ってしまう事故が起きます。私はモーダルの背景で実際にこれを踏みました。
対策はpointer-events: noneを足すか、visibilityと併用することです。キーボード操作とスクリーンリーダーからも外したいなら、後者を選びます。
▼フェードさせつつ、操作もできなくする書き方
.box {
opacity: 1;
visibility: visible;
transition: opacity 0.3s ease, visibility 0.3s;
}
.box.is-hidden {
opacity: 0;
visibility: hidden;
}
visibilityは中間の値を持たない(visibleかhiddenのどちらか)プロパティですが、transitionに並べて書くと切り替わるタイミングをopacityの完了まで遅らせられます。これでフェードアウトの途中で要素が消える現象を防げます。
この書き方はjQueryでモーダルウィンドウをさくっと作るでも使っています。
4パターンの動作デモ
「領域が詰まるか」と「アニメーションが付くか」の組み合わせで4通りになります。実際の動きはCodePenで確認してください。
①領域が詰まる・アニメーションあり
See the Pen
rNadzmZ by りん@webコーダー (@rinblog0408)
on CodePen.
非表示にすると領域が詰まり、切り替えにアニメーションが付きます。私はLP制作でよく使う組み合わせです。面積の大きい要素が予告なく消えると読み手が驚くため、大きなブロックほどアニメーションを入れています。
②領域が詰まる・アニメーションなし
See the Pen
MWYVvLz by りん@webコーダー (@rinblog0408)
on CodePen.
非表示にすると領域が詰まり、切り替えは即座に行われます。小さな要素の出し分けや、フォームのエラー文の表示など、演出が不要な場面はこれで十分です。
③領域が残る・アニメーションあり
See the Pen
xxbWXEw by りん@webコーダー (@rinblog0408)
on CodePen.
非表示にしても領域は残り、フェードで切り替わります。周りのレイアウトを1pxも動かしたくないときは、この組み合わせを選びます。ホバーで内容を差し替える表現とも相性が良く、似た考え方はホバーしたときにCSSだけで画像を切り替える方法でも使っています。
④領域が残る・アニメーションなし
See the Pen
oNgqGWY by りん@webコーダー (@rinblog0408)
on CodePen.
非表示にしても領域は残り、切り替えは即座に行われます。表の中身の出し分けのように、位置がずれると読みにくくなる箇所で使っています。
結局どれを使えばいいのか
迷ったときの判断は、「非表示のときに場所を空けるか」を先に決めると一本に絞れます。
▼目的別の選び方
- 閉じたら詰めたい(アコーディオン・メニュー)→
display: none - 閉じても場所は残したい(表・グリッド)→
visibility: hidden - ふわっと消したい(モーダル・ツールチップ)→
opacity: 0+visibility: hidden - 見た目だけ消して、読み上げには残したい →
opacity: 0単体
最後の1つは用途が限られます。見えていないのにスクリーンリーダーが読み上げる状態は、多くの場合は不具合です。意図してそうする場面以外ではvisibilityを併用してください。
JavaScriptやjQueryでも同じことは実現できますが、状態の切り替えをクラスの付け外しだけで済ませられる分、私はCSS側に寄せるほうが保守しやすいと感じています。