- CSS
display:noneとvisibility:hiddenの違い|非表示比較表
display:none は要素を「ないもの」として扱うためスペースも残りません。visibility:hidden は見えなくなるだけでスペースは残ります。opacity:0 は透明になるだけで、クリックもキーボード操作もできる状態のままです。
この記事では、非表示にする方法を「スペースが残るか・クリックできるか・Tabキーで選択されるか・読み上げられるか・transition が効くか」の5項目で1枚の比較表にまとめました。hidden 属性、content-visibility、視覚的にだけ隠すクラス(sr-only)も同じ表に並べています。後半では、display:none の要素をフェード表示させる transition-behavior: allow-discrete と @starting-style のコピペコードを、不透明度の変化を実際に測った結果つきで紹介します。出力はすべて Chromium 141(Playwright 経由)で確かめたもので、Firefox・Safari と、読み上げソフトの実機では動かしていません。
非表示にする方法の比較表
| 方法 | スペース | マウスでクリック | Tabキーで選択 | 読み上げ | transition |
|---|---|---|---|---|---|
display: none |
残らない | できない | されない | されない | allow-discrete を付けると効く |
visibility: hidden |
残る | できない | されない | されない | 効く(切り替えの時点をずらせる) |
opacity: 0 |
残る | できる | される | される | 効く(なめらかに変化) |
hidden 属性 |
残らない | できない | されない | されない | display:none と同じ |
content-visibility: hidden |
箱は残る(中身は高さ0扱い) | できない | されない | されない | allow-discrete を付けると効く |
| sr-only クラス | 残らない | できない | される | される | 対象外 |
「読み上げ」の列は、Chromium 141 のアクセシビリティツリー(読み上げソフトに渡される情報)にボタンと文章が残っているかで判定しました。
迷ったときの目安は次の3つです。
- 完全に取り除きたい →
display: none(または hidden 属性) - 場所を空けたまま見えなくしたい →
visibility: hidden - なめらかに消したい・現したい →
opacityとvisibilityを組み合わせる
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「display:noneとvisibility:hiddenの違い|非表示比較表」
比較表の根拠:実際に測ったコードと結果
次のHTMLは、同じ箱(高さ 50px、中にボタンと文章)に各方法を当て、すぐ下の要素の位置・その座標にある要素・フォーカスできるか・innerText に含まれるかを調べるものです。保存してブラウザで開くと一覧が出ます。
<!doctype html>
<html lang="ja">
<meta charset="utf-8">
<title>非表示の方法を比べる</title>
<style>
body { margin: 0; font: 16px/1.5 sans-serif; }
.box { height: 50px; }
.sr-only {
position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
}
</style>
<div id="stage"></div>
<pre id="out"></pre>
<script>
const cases = [
["通常表示", ''],
["display:none", 'style="display:none"'],
["visibility:hidden", 'style="visibility:hidden"'],
["opacity:0", 'style="opacity:0"'],
["hidden属性", 'hidden'],
["content-visibility:hidden", 'style="content-visibility:hidden"'],
["sr-only", 'data-sr'],
];
const stage = document.getElementById("stage");
const out = cases.map(([name, attr]) => {
stage.innerHTML =
'<div class="box" ' + attr + '><button id="t">対象</button> 隠す文</div><div id="next">次の要素</div>';
if (attr === "data-sr") stage.firstChild.className = "sr-only";
const next = document.getElementById("next").getBoundingClientRect().top;
const hit = document.elementFromPoint(20, 20); // 通常表示のときボタンがある位置
document.getElementById("t").focus();
const focused = document.activeElement.id === "t";
return name.padEnd(28)
+ "次の要素の上端=" + String(next).padStart(2) + "px"
+ " (20,20)にある要素=" + (hit.id || hit.tagName).padEnd(5)
+ " focus()=" + (focused ? "できる" : "できない")
+ " innerText=" + (stage.innerText.includes("隠す文") ? "含む" : "含まない");
});
stage.innerHTML = "";
document.getElementById("out").textContent = out.join("\n");
</script>
</html>
Chromium 141 での表示です。
通常表示 次の要素の上端=50px (20,20)にある要素=t focus()=できる innerText=含む
display:none 次の要素の上端= 0px (20,20)にある要素=next focus()=できない innerText=含まない
visibility:hidden 次の要素の上端=50px (20,20)にある要素=stage focus()=できない innerText=含まない
opacity:0 次の要素の上端=50px (20,20)にある要素=t focus()=できる innerText=含む
hidden属性 次の要素の上端= 0px (20,20)にある要素=next focus()=できない innerText=含まない
content-visibility:hidden 次の要素の上端=50px (20,20)にある要素=DIV focus()=できない innerText=含まない
sr-only 次の要素の上端= 0px (20,20)にある要素=next focus()=できる innerText=含む
読み方を補足します。
- 次の要素の上端が 50px なら箱のスペースが残っており、0px なら詰まっています。
- (20,20) にある要素が
t(対象のボタン)なら、その場所をクリックするとボタンが反応します。next・stage・DIV(箱そのもの)なら、ボタンはクリックの対象になっていません。 - focus() が「できる」ものは、Tabキーでも選択されます。
同じ箱に対して、Playwright でマウスクリックと Tab キーを実際に送った結果もそろえました。
| 方法 | マウスクリックでボタンが反応 | 最初のボタンから Tab を押した移動先 | アクセシビリティツリー |
|---|---|---|---|
| 通常表示 | 反応した | 対象のボタン | ボタン・文章あり |
display: none |
反応しない | 対象を飛ばして最後のボタン | なし |
visibility: hidden |
反応しない | 対象を飛ばして最後のボタン | なし |
opacity: 0 |
反応した | 対象のボタン | ボタン・文章あり |
hidden 属性 |
反応しない | 対象を飛ばして最後のボタン | なし |
content-visibility: hidden |
反応しない | 対象を飛ばして最後のボタン | なし |
| sr-only クラス | 反応しない | 対象のボタン | ボタン・文章あり |
それぞれの方法の特徴
display: none:要素をないものとして扱う
要素とその子孫が、レイアウトからまるごと外れます。 スペースは残らず、後ろの要素が詰まります。クリックも Tab も読み上げも対象外です。
.is-hidden {
display: none;
}
押さえておきたい性質は次のとおりです。
| 性質 | 測定結果 |
|---|---|
| 大きさ・位置の取得 | getBoundingClientRect() は幅・高さとも 0 |
| 子要素で打ち消せるか | できない(子に display: block を書いても幅 0 のまま) |
| DOM には残るか | 残る(textContent には文章が含まれ、innerText には含まれない) |
JavaScript の element.click() |
クリックイベントは発火する |
最後の行は意外に感じるかもしれません。JavaScript から click() を呼ぶと、どの非表示方法でもイベントは発火しました。 画面上で人が操作できないことと、スクリプトから呼べないことは別、という点は覚えておくと役立ちます。
なお、display の値そのものの種類(block・inline・flex など)は別の話題なので、ここでは none に絞っています。
visibility: hidden:場所を空けたまま見えなくする
見えなくなりますが、箱の大きさはそのまま残ります。 表の列をそろえたいときや、表示・非表示でレイアウトを動かしたくないときに向いています。クリック・Tab・読み上げは対象外になります。
.is-invisible {
visibility: hidden;
}
visibility には、他の方法にない特徴があります。親が hidden でも、子に visibility: visible を書くとその子だけ表示できます。
<div style="visibility: hidden;">
親の文章(見えない)
<button style="visibility: visible;">この子だけ見える</button>
</div>
このボタンは表示され、Playwright からクリックできることを確認しました。opacity や display では同じことはできません(opacity: 0 の親の中で子に opacity: 1 を指定しても、撮った画像は全面が白でした)。
visibility: collapse という値もあります。表の行や列に使うための値で、通常の div に指定した場合は hidden と同じ結果(スペースが残り、クリック・Tab の対象外)でした。
opacity: 0:透明にするだけ
見えなくなるだけで、要素はその場所で普通に働き続けます。 スペースが残り、クリックでき、Tab で選択され、読み上げの対象にもなります。フェードの途中経過として使うのが本来の用途です。
.is-transparent {
opacity: 0;
}
opacity: 0 だけで「消した」つもりにすると、見えないボタンが押せてしまう状態になります。消したい場合は、後述のように visibility と組み合わせます。
hidden 属性:HTML 側で非表示にする
HTML の属性で「この要素は今は関係ない」と示す方法です。 ブラウザは既定で display: none を当てます。
<p hidden>あとで表示する文章</p>
// 表示・非表示の切り替え
element.hidden = false;
element.hidden = true;
Chromium 141 では、hidden 属性の付いた要素の計算後の display は none で、結果は display:none とすべて同じでした。クラスの付け外しではなく属性1つで済むので、JavaScript での切り替えが短く書けます。
注意点は、自分の CSS で display を指定すると、そちらが優先されることです(後述の「hidden 属性を付けたのに消えない」)。
hidden="until-found":ページ内検索で見つかると開く
hidden="until-found" を指定すると、ふだんは隠しつつ、ページ内検索などで中の文章が見つかったときに表示させる動きになります。Chromium 141 では、この属性を付けた要素は display: block のまま content-visibility: hidden が当たっていました。クリック・Tab・読み上げは対象外でした。ページ内検索で実際に開く動作と、他のブラウザでの対応は未実行です。
content-visibility: hidden:中身の描画を省く
要素の箱は残したまま、中身の描画を省く指定です。 MDN では、hidden にすると中身がページ内検索・Tab 移動・選択の対象から外れ、アクセシビリティツリーからも外れると説明されています。今回の測定でも、クリック・Tab・読み上げは対象外でした。
.panel[data-state="closed"] {
content-visibility: hidden;
}
スペースの扱いには特徴があります。高さを指定していない要素では、中身の高さが 0 として扱われます(段落を2つ入れた div の高さが 0px、次の要素の上端も 0px)。高さを指定している要素では、その高さの箱が残ります(比較表の測定では 50px のまま)。
display:none との違いは、中身の描画状態をブラウザが保持できる点で、何度も開閉する大きなパネルを素早く再表示したい場合に向いています。MDN では 2024年9月から主要ブラウザの最新版で使える機能(Baseline 2024)と案内されています。通常の表示・非表示には display:none や hidden 属性で十分です。
sr-only クラス:見た目だけ隠して読み上げには残す
画面には見せず、読み上げソフトには伝えたい文章に使うクラスです。 アイコンだけのボタンに説明を添える、表の見出しを補う、といった用途があります。
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip-path: inset(50%);
white-space: nowrap;
border: 0;
}
<button type="button">
<svg aria-hidden="true" width="16" height="16"><!-- アイコン --></svg>
<span class="sr-only">メニューを開く</span>
</button>
このクラスを当てた要素は、スペースを取らず(次の要素の上端 0px)、マウスではクリックできず、アクセシビリティツリーには文章が残りました。中のボタンは Tab で選択されるので、sr-only の中に操作できる要素を入れると「見えないのにフォーカスが止まる」状態になります。文章だけに使うのが基本です。
その他の隠し方も同じ項目で比べる
比較表の6つ以外も、同じ条件で測りました。
| 方法 | スペース | マウスでクリック | Tabキーで選択 | アクセシビリティツリー |
|---|---|---|---|---|
aria-hidden="true" |
残る(見えている) | できる | される | 外れる |
inert 属性 |
残る(見えている) | できない | されない | 外れる |
opacity: 0 + pointer-events: none |
残る | できない | される | 残る |
transform: scale(0) |
残る | できない | される | 残る |
height: 0 + overflow: hidden |
残らない | できない | される | 残る |
position: absolute; left: -9999px |
残らない | できない | される | 残る |
aria-hidden="true"は見た目を変えず、読み上げからだけ外す属性です。装飾用のアイコンなどに使います。Tab では選択されるままなので、ボタンやリンクには付けないのが原則です(フォーカスが当たっている間は、Chromium がその要素をツリーに戻す動きも見られました)。inertは見た目を変えず、操作と読み上げをまとめて止める属性です。モーダルを開いている間の背景部分などに向いています。- 下の4つは見えなくなっても Tab で選択されます。 「見えないのにフォーカスが止まる」症状の原因になりやすい組み合わせです。
選び方のフローチャート
flowchart TD
A[要素を隠したい] --> B{読み上げには残す?}
B -- 残す --> C[sr-only クラス]
B -- 残さない --> D{スペースを残す?}
D -- 残す --> E[visibility: hidden]
D -- 残さない --> F{フェードさせる?}
F -- させない --> G[display: none または hidden 属性]
F -- させる --> H["allow-discrete と starting-style、または opacity と visibility"]
| 場面 | 向いている方法 |
|---|---|
| タブの切り替えで、選ばれていないパネルを消す | hidden 属性/display: none |
| 条件で出し分ける入力欄やメッセージ | hidden 属性/display: none |
| 表のセルを空欄にしつつ列幅を保つ | visibility: hidden |
| メニューやツールチップをふわっと出す | opacity + visibility、または allow-discrete |
| アイコンボタンに読み上げ用の説明を付ける | sr-only クラス |
| 飾りのアイコンを読み上げから外す | aria-hidden="true" |
| モーダル表示中の背景を操作できなくする | inert 属性 |
transitionが効くか:非表示とアニメーション
opacity はなめらかに変化し、visibility は「切り替わる時点」を遅らせることができ、display は allow-discrete を付けたときだけ切り替えを待ってくれます。 0.4秒の transition を付けてクラスを切り替え、途中の値を測りました。
| 変化 | 0ms | 100ms | 200ms | 300ms | 450ms |
|---|---|---|---|---|---|
| opacity 1 → 0 | 1.00 | 0.59 | 0.20 | 0.04 | 0.00 |
| visibility visible → hidden | visible | visible | visible | visible | hidden |
| visibility hidden → visible | hidden | visible | visible | visible | visible |
| display block → none(allow-discrete なし) | none | none | none | none | none |
| display block → none(allow-discrete あり、opacity も 0 へ) | block/1.00 | block/0.59 | block/0.20 | block/0.04 | none/0.00 |
- visibility は、消えるときは transition の終わりに hidden へ、現れるときは始まってすぐ visible へ切り替わります。「見えている時間が長くなる側」に寄る動きです。
- display は通常、transition を書いていても即座に none になります(transitionrun などのイベントも発火しませんでした)。opacity を一緒に変えていても、要素が先に消えるのでフェードは見えません。
allow-discreteを付けると、display も visibility と同じように「終わりまで block のまま待つ」ようになり、フェードアウトが見えます。
定番:opacity と visibility を組み合わせる
広い環境で使える、定番の書き方です。
.menu {
opacity: 0;
visibility: hidden;
transition: opacity 0.4s, visibility 0.4s;
}
.menu.is-open {
opacity: 1;
visibility: visible;
}
消すときの測定値は、opacity が 1.00 → 0.59 → 0.20 → 0.04 → 0.00 と下がり、visibility は 450ms の時点で hidden に切り替わりました。フェードが終わってから操作と読み上げの対象外になるので、「見えないのに押せる」状態が残りません。スペースは残り続けるため、メニューやツールチップのように position: absolute で浮かせる部品に向いています。
display:noneの要素をフェード表示するコピペコード
スペースごと消したい要素をフェードさせるには、transition-behavior: allow-discrete と @starting-style を組み合わせます。
<!doctype html>
<html lang="ja">
<meta charset="utf-8">
<title>display:none からフェード表示</title>
<style>
.panel {
display: none;
opacity: 0;
padding: 16px;
background: #eef6ff;
border: 1px solid #9cc2f0;
border-radius: 8px;
/* 消えるとき:opacity が 0 になり切ってから display:none に切り替える */
transition: opacity 0.4s, display 0.4s allow-discrete;
}
.panel.is-open {
display: block;
opacity: 1;
}
/* 現れるとき:display:none から出た瞬間の「出発点」 */
@starting-style {
.panel.is-open {
opacity: 0;
}
}
</style>
<button type="button" id="toggle" aria-expanded="false" aria-controls="panel">お知らせを開く</button>
<div class="panel" id="panel">お知らせの本文です。</div>
<script>
const btn = document.getElementById("toggle");
const panel = document.getElementById("panel");
btn.addEventListener("click", () => {
const open = panel.classList.toggle("is-open");
btn.setAttribute("aria-expanded", open);
});
</script>
</html>
仕組みは2つです。
| 仕組み | 役割 |
|---|---|
display 0.4s allow-discrete |
消えるとき、0.4秒のあいだ display を block のまま保ち、最後に none へ切り替える |
@starting-style { ... } |
display:none から現れた瞬間の「出発点」のスタイルを決める。ここから通常のスタイルへ transition する |
transition: opacity 0.4s, display 0.4s allow-discrete; は一括指定で、transition-behavior: allow-discrete; を別に書くのと同じ意味です。
Chromium 141 でボタンを押し、150ms ごとに「display/opacity」を測った結果です。条件を変えた2パターンも並べました。
| 条件 | 開くとき(0 → 150 → 300 → 450ms) | 閉じるとき(0 → 150 → 300 → 450ms) |
|---|---|---|
| 上のコードそのまま | block/0.00 → 0.65 → 0.96 → 1.00 | block/1.00 → 0.42 → 0.04 → none/0.00 |
| @starting-style を外す | block/1.00 のまま(一瞬で表示) | block/1.00 → 0.42 → 0.04 → none/0.00 |
| allow-discrete を外す | block/0.00 → 0.58 → 0.96 → 1.00 | すぐに none/0.00(一瞬で消える) |
現れるときのフェードは @starting-style が、消えるときのフェードは allow-discrete が担当していることが分かります。両方そろえて、行きも帰りもなめらかになります。
MDN の transition-behavior のページでは、2024年8月から主要ブラウザの最新版で使える機能(Baseline 2024)と案内されています。対応していないブラウザではフェードせずに即座に切り替わるだけなので、表示・非表示そのものは働きます。
popover をフェードさせる
HTML の popover 属性で出す要素は、最前面の層(トップレイヤー)に置かれます。フェードアウトの間もその層に留めるため、overlay も transition に加えます。
<!doctype html>
<html lang="ja">
<meta charset="utf-8">
<style>
[popover] {
opacity: 0;
transition: opacity 0.4s, display 0.4s allow-discrete, overlay 0.4s allow-discrete;
}
[popover]:popover-open {
opacity: 1;
}
@starting-style {
[popover]:popover-open {
opacity: 0;
}
}
</style>
<button type="button" popovertarget="tip">開く</button>
<div popover id="tip">お知らせ</div>
</html>
Chromium 141 で 150ms ごとに測ると、開くときは block/0.00 → 0.65 → 0.96 → 1.00、閉じるときは block/1.00 → 0.29 → 0.03 → none/0.00 でした。JavaScript を1行も書かずに、開閉とフェードの両方が動きます。
content-visibility も allow-discrete で待たせられる
transition: opacity 0.4s, content-visibility 0.4s allow-discrete; を指定して hidden へ切り替えると、opacity が 1.00 → 0.59 → 0.10 → 0.01 → 0.00 と変化し、content-visibility の transition イベント(transitionrun・transitionend)も発火しました。
症状別の直し方
消したのにクリックできる
原因は opacity: 0 だけで隠していることです。透明になっても、要素はその場所でクリックを受け取ります。
直す前:
.tooltip { opacity: 0; transition: opacity 0.3s; }
.tooltip.is-open { opacity: 1; }
直した後:
.tooltip { opacity: 0; visibility: hidden; transition: opacity 0.3s, visibility 0.3s; }
.tooltip.is-open { opacity: 1; visibility: visible; }
pointer-events: none を足す方法もありますが、測定のとおりマウスのクリックは防げても Tab では選択されます。 キーボード操作も含めて止めるには visibility: hidden を組み合わせるのが確実です。
見えないのに Tab キーでフォーカスが止まる
opacity: 0、transform: scale(0)、height: 0、画面外への移動は、どれも Tab の対象のままです。次のいずれかに変えます。
| 直し方 | 向いている場面 |
|---|---|
visibility: hidden を足す |
フェードと両立させたい |
display: none/hidden 属性にする |
スペースも不要 |
inert 属性を付ける |
見た目はそのままで、操作だけ止めたい |
高さを 0 にして開閉するアコーディオンでは、閉じている間だけ中身に visibility: hidden を足す(開閉と同じ時間の transition を付ける)と、フォーカスが中に入らなくなります(この組み合わせは書き方の提案で、今回は未実行です)。
消したら下の要素が詰まってレイアウトが動く
display: none はスペースを残さないためです。場所を保ちたいなら visibility: hidden に変えます。切り替えのたびに周りが動いて読みにくい、という場面で有効です。
display:none にフェードが効かない
display は、通常の transition では即座に切り替わります。 次の2通りから選びます。
| 方法 | コード | 特徴 |
|---|---|---|
| opacity + visibility | 前述の「定番」 | 幅広い環境で動く。スペースは残る |
| allow-discrete + @starting-style | 前述のコピペコード | スペースごと消せる。対応ブラウザではフェードし、その他では即切り替え |
hidden 属性を付けたのに消えない
自分の CSS で display を指定していると、hidden 属性の既定のスタイルより優先されます。
<style>
.row { display: flex; }
</style>
<div class="row" hidden>消えてほしい</div>
Chromium 141 では、この div の計算後の display は flex、高さは 18px で、表示されたままでした。次のどちらかで直ります。
/* その部品だけ直す */
.row[hidden] { display: none; }
/* サイト全体で hidden 属性を優先させる */
[hidden] { display: none !important; }
.row[hidden] { display: none; } を足すと、display は none、高さは 0px になりました。2つ目のサイト全体向けの書き方でも、同じ div の display は none、高さは 0px になりました。hidden="until-found" を使う場合は、この全体指定が until-found の要素にも当たる点に注意してください。
隠した文章が読み上げられる/読み上げてほしいのに読まれない
| 状態 | 原因 | 直し方 |
|---|---|---|
| 見えない文章が読み上げられる | opacity: 0 や画面外への移動で隠している | display: none/visibility: hidden にする |
| 読み上げてほしい補足が読まれない | display: none や visibility: hidden で隠している | sr-only クラスに変える |
| 飾りのアイコンが読み上げられる | 読み上げ対象のまま | aria-hidden="true" を付ける |
JavaScript で大きさが取れない
display: none の要素は、幅・高さ・位置がすべて 0 として返ります。 開く前に大きさを測りたい場合は、visibility: hidden で配置してから測ると、見せずに実寸を取得できます(visibility: hidden では、ボタンの矩形が通常表示と同じ幅 81px・高さ 24px で返りました)。
コピペ用:非表示のユーティリティクラス
ここまでの内容を、用途別のクラスにまとめました。
/* 1. 完全に取り除く(スペースなし・操作なし・読み上げなし) */
.is-hidden { display: none; }
[hidden] { display: none !important; }
/* 2. 場所を空けたまま見えなくする */
.is-invisible { visibility: hidden; }
/* 3. 見た目だけ隠して読み上げには残す(文章用) */
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip-path: inset(50%);
white-space: nowrap;
border: 0;
}
/* 4. フェードで出し入れする(スペースは残る) */
.fade {
opacity: 0;
visibility: hidden;
transition: opacity 0.4s, visibility 0.4s;
}
.fade.is-open {
opacity: 1;
visibility: visible;
}
/* 5. 画面幅で出し分ける */
@media (max-width: 767px) {
.hide-on-narrow { display: none; }
}
@media (min-width: 768px) {
.hide-on-wide { display: none; }
}
1〜4 は、それぞれ前述の測定で動作を確かめた書き方です。5 の画面幅による出し分けは一般的な書き方の紹介で、今回は未実行です。画面幅で消した要素も display: none なので、読み上げと Tab の対象から外れます。同じ内容を幅別に2つ用意する場合は、片方だけが有効になるため、読み上げが二重になることはありません。
| クラス | スペース | 操作 | 読み上げ | 主な用途 |
|---|---|---|---|---|
.is-hidden |
残らない | できない | されない | 出し分け、タブのパネル |
.is-invisible |
残る | できない | されない | レイアウトを動かしたくない場面 |
.sr-only |
残らない | 文章用 | される | アイコンボタンの説明 |
.fade |
残る | 開いている間だけ | 開いている間だけ | メニュー、ツールチップ |
innerText と textContent の違いにも表れる
非表示の方法は、JavaScript で文字を取り出すときにも影響します。
| 方法 | innerText |
textContent |
|---|---|---|
| display: none | 含まない | 含む |
| visibility: hidden | 含まない | 含む |
| opacity: 0 | 含む | 含む |
| hidden 属性 | 含まない | 含む |
| content-visibility: hidden | 含まない | 含む |
| sr-only クラス | 含む | 含む |
innerText は「表示されている文字」を返し、textContent は DOM にある文字をすべて返します。 隠した文章まで取得したいときは textContent を使います。
よくある質問
Q. display:none と visibility:hidden はどちらが軽いですか? A. 今回は速度の測定はしていません。考え方としては、display:none はレイアウトの計算から外れ、visibility:hidden は場所の計算が残ります。ふつうの数の要素であれば体感の差は出にくいので、「スペースを残すかどうか」で選ぶのが分かりやすいです。
Q. opacity:0 で隠すのは避けたほうがよいですか?
A. フェードの途中経過として使うのは適した使い方です。opacity:0 のまま置いておくと、クリックでき、Tab で選択され、読み上げもされるので、visibility: hidden を組み合わせて「消えている間は操作できない」状態にすると安心です。
Q. hidden 属性と display:none はどう使い分けますか?
A. 結果は同じです。「この内容は今は関係ない」という意味を HTML に持たせたいとき、JavaScript から element.hidden = true と短く書きたいときは hidden 属性が向いています。画面幅に応じた出し分けのように、見た目の都合で消す場合は CSS の display:none を使います。
Q. @starting-style はなぜ必要なのですか? A. display:none の要素には「直前のスタイル」がないため、現れた瞬間から最終のスタイルになり、変化の出発点がありません。@starting-style で出発点(たとえば opacity: 0)を決めると、そこから transition が始まります。測定でも、@starting-style を外すと開くときだけフェードしなくなりました。
まとめ
- display:none はスペースごと消え、visibility:hidden はスペースを残して見えなくし、opacity:0 は透明になるだけ
- opacity:0 はクリックでき、Tab で選択され、読み上げもされる。 消すときは visibility:hidden と組み合わせる
- hidden 属性は display:none と同じ結果。 自分の CSS の display 指定が優先されるので
[hidden]用の指定を添える - sr-only は見た目だけ隠して読み上げに残すクラス。文章用で、操作できる要素は入れない
- フェードの定番は opacity + visibility。 スペースごと消すなら allow-discrete + @starting-style
- @starting-style が現れるときのフェード、allow-discrete が消えるときのフェードを担当する
- 「見えないのにフォーカスが止まる」ときは、scale(0)・height:0・画面外移動が Tab の対象のままである点を確認する
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「display:noneとvisibility:hiddenの違い|非表示比較表」
株式会社RJCでは、CSSの非表示の使い分けやアクセシビリティへの配慮を仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。