自分だけのブログを始めませんか?
WordPressブログなら、あなたのコーディング知識でデザインのカスタマイズが自由自在。
ブログデビューに最適な初心者向けサーバー環境を徹底比較しました。

WordPressブログなら、あなたのコーディング知識でデザインのカスタマイズが自由自在。
ブログデビューに最適な初心者向けサーバー環境を徹底比較しました。
要素の表示・非表示を制御するvisibilityは、レイアウトの形を崩さずに要素を隠すことができる重要なプロパティです。
本記事では、display: noneやopacityとの明確な使い分け、フェードアニメーションの実装、JavaScriptとの連携方法まで解説します。
Web制作において、要素の表示・非表示をコントロールする場面は多々あります。
その際に活躍する代表的なプロパティがvisibilityです。
visibilityは、値や選択肢を正しく理解することで、ブロックの表示制御やボタンの表示制御を意図通りに行うことができます。
ここでは、基本的なvisibilityの使い方、実務で直面するレイアウト上の注意点を解説します。
hiddenと表示するvisiblevisibilityプロパティの基本的な使い方は、要素を「見えなくする(隠す)」か「見えるようにする」かの切り替えです。
通常、すべての要素はデフォルトでvisibility: visible;(表示)になっています。
これをvisibility: hidden;に変更するだけで、要素は画面上から姿を消します。
表示・非表示を切り替えるには、要素を視覚的に隠しつつ、ユーザーの誤操作(クリック等)も防ぎたい場合はopacity: 0;ではなくvisibility: hidden;を使うことです。
ホバーなどのアクションで再表示させたい場合は、CSSの疑似クラス(:hover)などを利用してvisibility: visible;に上書きして状態を切り替えることです。
⭕️ ホバーして隠してみよう!透明(opacity)と非表示(visibility)の違いを体感しろ!
❌ 罠(opacity: 0)
※箱にホバーすると透明になりますが、
ボタンのある場所をクリックできてしまいます。
⭕️ 成功(visibility: hidden)
※箱にホバーすると非表示になり、
ボタンも完全にクリックできなくなります。
<div class="vis-basic-wrapper">
<p class="vis-basic-caption">⭕️ ホバーして隠してみよう!透明(opacity)と非表示(visibility)の違いを体感しろ!</p>
<div class="vis-demo-area">
<div class="demo-vis-box is-trap-opacity">
<p class="vis-title">❌ 罠(opacity: 0)</p>
<div class="target-element target-opacity">
<button class="demo-btn">透明でも押せるボタン</button>
</div>
<p class="vis-desc">※箱にホバーすると透明になりますが、<br>ボタンのある場所をクリックできてしまいます。</p>
</div>
<div class="demo-vis-box is-success-hidden">
<p class="vis-title">⭕️ 成功(visibility: hidden)</p>
<div class="target-element target-hidden">
<button class="demo-btn">完全に無効化されるボタン</button>
</div>
<p class="vis-desc">※箱にホバーすると非表示になり、<br>ボタンも完全にクリックできなくなります。</p>
</div>
</div>
<div class="vis-code-area">
<span class="hl-comment">/* ❌ 罠:opacity:0 は透明なだけでクリック判定が残る */</span><br>
<span class="hl-blue">.box-opacity:hover .target</span> {<br>
<span class="hl-green">opacity:</span> <span class="hl-red">0;</span> <span class="hl-comment">/* 🚨 見えないのに押せてしまうバグの温床 */</span><br>
}<br><br>
<span class="hl-comment">/* ⭕️ 隠すなら visibility: hidden; で操作も無効化する! */</span><br>
<span class="hl-blue">.box-hidden:hover .target</span> {<br>
<span class="hl-green">visibility:</span> <span class="hl-red">hidden;</span> <span class="hl-comment">/* 💡 姿を消し、クリックも不可能にする */</span><br>
}<br><br>
<span class="hl-comment">/* 💡 再表示させる時は visible に戻す */</span><br>
<span class="hl-blue">.target.is-active</span> {<br>
<span class="hl-green">visibility:</span> <span class="hl-red">visible;</span><br>
}
</div>
</div>.vis-basic-wrapper {
background-color: #f8f9fa;
padding: 30px;
border-radius: 8px;
border: 1px solid #dee2e6;
}
.vis-basic-caption {
font-size: 14px;
font-weight: bold;
margin-bottom: 20px;
color: #198754;
text-align: center;
}
.vis-demo-area {
display: flex;
flex-wrap: wrap;
gap: 30px;
background-color: #e9ecef;
padding: 30px 20px;
border-radius: 8px;
border: 1px dashed #adb5bd;
margin-bottom: 20px;
justify-content: center;
}
.demo-vis-box {
width: 100%;
max-width: 300px;
background-color: #ffffff;
padding: 15px;
border: 1px solid #ced4da;
border-radius: 4px;
text-align: center;
}
.vis-title {
margin-top: 0;
margin-bottom: 15px;
font-size: 13px;
font-weight: bold;
color: #dc3545;
border-bottom: 1px dashed #ced4da;
padding-bottom: 5px;
}
.demo-vis-box.is-success-hidden .vis-title {
color: #198754;
}
.target-element {
background-color: #e2e3e5;
padding: 20px;
border-radius: 4px;
margin-bottom: 15px;
}
.demo-btn {
background-color: #0d6efd;
color: #ffffff;
border: none;
padding: 10px 15px;
border-radius: 4px;
font-weight: bold;
cursor: pointer;
}
.demo-btn:hover {
background-color: #0a58ca;
}
.vis-desc {
margin: 0;
font-size: 11px;
color: #6c757d;
line-height: 1.5;
}
/* === ❌ 罠:opacityで隠す(ホバー時) === */
.is-trap-opacity:hover .target-opacity {
/* 🚨 見えないだけで実体がありクリックできてしまう */
opacity: 0;
}
/* === ⭕️ 成功:visibilityで隠す(ホバー時) === */
.is-success-hidden:hover .target-hidden {
/* 💡 完全に非表示になり、操作も受け付けない */
visibility: hidden;
}
/* =コード解説エリア(エディタ風)= */
.vis-code-area {
background-color: #282c34;
color: #abb2bf;
padding: 20px;
border-radius: 6px;
font-family: monospace;
font-size: 13px;
line-height: 1.6;
border-left: 4px solid #0d6efd;
overflow-x: auto;
text-align: left;
}
.hl-blue { color: #61afef; font-weight: bold; }
.hl-red { color: #e06c75; font-weight: bold; }
.hl-green { color: #98c379; font-weight: bold; }
.hl-comment { color: #6c757d; font-style: italic; }visibility: hidden;を使う上で覚えておかなければならない特徴は、「要素は見えなくなるが、要素が占めていた領域はそのまま残る」ということです。
まるで透明マントを被ったかのように、そこに存在し続けます。
この性質を知らずにスペースを詰めずに隠す方法を探して混乱するケースが多いです。
スペースごと消し去りたい場合は、visibilityではなくdisplay: none;を使用する必要があります。
表示制御プロパティを使い分けるには、『見えなくした上で、他の要素を詰めてレイアウトを再計算させたい』場合はdisplay: none;を使うことです。
一方、『レイアウトの形は崩さず、中身だけを視覚的に消したい』場合はvisibility: hidden;を使うことです。
目的によってこの2つを明確に使い分けることがレイアウト崩れを防ぎます。
⭕️ ホバーして比較!「スペースが残るか・詰まるか」の違いを目に焼き付けろ!
💡 visibility: hidden
(ホバーで真ん中を隠す)
※レイアウトを保ったまま、
ポッカリと空白が残ります。
💡 display: none
(ホバーで真ん中を隠す)
※要素が完全に無かったことになり、
下の項目Cが上に詰まります。
<div class="vis-space-wrapper">
<p class="vis-space-caption">⭕️ ホバーして比較!「スペースが残るか・詰まるか」の違いを目に焼き付けろ!</p>
<div class="vis-space-demo-area">
<div class="space-box is-test-visibility">
<p class="space-title">💡 visibility: hidden<br><span class="small-note">(ホバーで真ん中を隠す)</span></p>
<div class="item-list">
<div class="item">項目 A</div>
<div class="item target-vis">項目 B (消える)</div>
<div class="item">項目 C</div>
</div>
<p class="space-desc">※レイアウトを保ったまま、<br>ポッカリと空白が残ります。</p>
</div>
<div class="space-box is-test-display">
<p class="space-title">💡 display: none<br><span class="small-note">(ホバーで真ん中を隠す)</span></p>
<div class="item-list">
<div class="item">項目 A</div>
<div class="item target-disp">項目 B (消える)</div>
<div class="item">項目 C</div>
</div>
<p class="space-desc">※要素が完全に無かったことになり、<br>下の項目Cが上に詰まります。</p>
</div>
</div>
<div class="vis-space-code-area">
<span class="hl-comment">/* 💡 用途:レイアウト(形)を絶対に崩したくない時 */</span><br>
<span class="hl-blue">.box-visibility:hover .target</span> {<br>
<span class="hl-green">visibility:</span> <span class="hl-red">hidden;</span> <span class="hl-comment">/* 💡 見えなくなるが、スペースはそのまま維持される */</span><br>
}<br><br>
<span class="hl-comment">/* 💡 用途:要素を消して、空いた隙間を詰めたい時 */</span><br>
<span class="hl-blue">.box-display:hover .target</span> {<br>
<span class="hl-green">display:</span> <span class="hl-red">none;</span> <span class="hl-comment">/* 💡 要素が存在しないものとして扱われ、レイアウトが詰められる */</span><br>
}
</div>
</div>.vis-space-wrapper {
background-color: #f8f9fa;
padding: 30px;
border-radius: 8px;
border: 1px solid #dee2e6;
}
.vis-space-caption {
font-size: 14px;
font-weight: bold;
margin-bottom: 20px;
color: #198754;
text-align: center;
}
.vis-space-demo-area {
display: flex;
flex-wrap: wrap;
gap: 30px;
background-color: #e9ecef;
padding: 30px 20px;
border-radius: 8px;
border: 1px dashed #adb5bd;
margin-bottom: 20px;
justify-content: center;
}
.space-box {
width: 100%;
max-width: 250px;
background-color: #ffffff;
padding: 15px;
border: 1px solid #ced4da;
border-radius: 4px;
text-align: center;
}
.space-title {
margin-top: 0;
margin-bottom: 15px;
font-size: 13px;
font-weight: bold;
color: #0d6efd;
}
.small-note {
font-size: 11px;
color: #666;
font-weight: normal;
}
.item-list {
background-color: #f1f3f5;
padding: 10px;
border-radius: 4px;
margin-bottom: 15px;
display: flex;
flex-direction: column;
gap: 10px;
}
.item {
background-color: #6c757d;
color: #ffffff;
padding: 10px;
border-radius: 4px;
font-size: 13px;
font-weight: bold;
transition: background-color 0.3s ease;
}
.item:nth-child(2) {
background-color: #fd7e14;
}
.space-desc {
margin: 0;
font-size: 11px;
color: #6c757d;
line-height: 1.5;
}
/* === 💡 特徴1:visibility: hidden === */
.is-test-visibility:hover .target-vis {
/* 💡 姿は消えるが、スペース(高さや幅)は残る */
visibility: hidden;
}
/* === 💡 特徴2:display: none === */
.is-test-display:hover .target-disp {
/* 💡 存在そのものが消え、下の要素が上に詰まる */
display: none;
}
/* =コード解説エリア(エディタ風)= */
.vis-space-code-area {
background-color: #282c34;
color: #abb2bf;
padding: 20px;
border-radius: 6px;
font-family: monospace;
font-size: 13px;
line-height: 1.6;
border-left: 4px solid #0d6efd;
overflow-x: auto;
text-align: left;
}
/* =コード解説エリア(エディタ風)= */
.vis-code-area {
background-color: #282c34;
color: #abb2bf;
padding: 20px;
border-radius: 6px;
font-family: monospace;
font-size: 13px;
line-height: 1.6;
border-left: 4px solid #0d6efd;
overflow-x: auto;
text-align: left;
}
.hl-blue { color: #61afef; font-weight: bold; }
.hl-red { color: #e06c75; font-weight: bold; }
.hl-green { color: #98c379; font-weight: bold; }
.hl-comment { color: #6c757d; font-style: italic; }displayについて詳しく知りたい人は「【CSS】display: blockとは?inlineやflexとの違いと種類」を一読ください。
Webデザインを学び始めると、ぶつかるのが「要素を隠すプロパティが多すぎて、どれを使えばいいか分からない」という壁です。
画面から要素を消す方法には、主にvisibility: hidden;、display: none;、opacity: 0;の3つが存在します。
これらは「見た目が消える」という結果は同じでも、ブラウザ内部での扱いやレイアウトへの影響が全く異なります。
ここでは、それぞれの特性と使い分け基準を実務に即して比較します。
display: noneとの使い分けopacity: 0との違いWeb制作において、不要になった要素を画面から消し去る際に使われるのがdisplay: none;です。
この2つの違いは、「要素が元々あったスペース(領域)を維持するかどうか」です。
領域に関する表示制御は、スマホのハンバーガーメニューなどで『要素そのものをHTMLから無かったことにして、レイアウトを上に詰めたい』場合はdisplay: none;を使うことです。
逆に、表組み(テーブル)の空セルやあとでフワッと表示させる『場所取り(スペース確保)』が必要な場合は、visibility: hidden;を使うことです。
⭕️ ホバーして比較!「スペースが残るか、詰まるか」の違いはレイアウトに直結する!
💡 visibility: hidden
※カード2のスペースは維持されたまま、
透明人間のように存在し続けます。
💡 display: none
※カード2の存在が完全に抹消され、
下のカード3が上にスライドして詰まります。
<div class="compare-display-wrapper">
<p class="compare-display-caption">⭕️ ホバーして比較!「スペースが残るか、詰まるか」の違いはレイアウトに直結する!</p>
<div class="compare-demo-area">
<div class="compare-box is-visibility">
<p class="compare-title">💡 visibility: hidden</p>
<div class="card-list">
<div class="card">カード 1</div>
<div class="card target-vis">カード 2 (ホバーで隠す)</div>
<div class="card">カード 3</div>
</div>
<p class="compare-desc">※カード2のスペースは維持されたまま、<br>透明人間のように存在し続けます。</p>
</div>
<div class="compare-box is-display">
<p class="compare-title">💡 display: none</p>
<div class="card-list">
<div class="card">カード 1</div>
<div class="card target-disp">カード 2 (ホバーで消滅)</div>
<div class="card">カード 3</div>
</div>
<p class="compare-desc">※カード2の存在が完全に抹消され、<br>下のカード3が上にスライドして詰まります。</p>
</div>
</div>
<div class="compare-code-area">
<span class="hl-comment">/* 💡 visibility:場所取り(レイアウト)を維持したい場合 */</span><br>
<span class="hl-blue">.box-visibility:hover .target</span> {<br>
<span class="hl-green">visibility:</span> <span class="hl-red">hidden;</span> <span class="hl-comment">/* 💡 姿だけを消し、高さや幅はそのまま残る */</span><br>
}<br><br>
<span class="hl-comment">/* 💡 display:要素を詰めて再配置させたい場合 */</span><br>
<span class="hl-blue">.box-display:hover .target</span> {<br>
<span class="hl-green">display:</span> <span class="hl-red">none;</span> <span class="hl-comment">/* 🚨 要素がDOMツリーから外れたように振る舞い、レイアウトが詰まる */</span><br>
}<br>
<span class="hl-comment">/* ※注意:displayプロパティは transition(アニメーション)と組み合わせることができません */</span>
</div>
</div>.compare-display-wrapper {
background-color: #f8f9fa;
padding: 30px;
border-radius: 8px;
border: 1px solid #dee2e6;
}
.compare-display-caption {
font-size: 14px;
font-weight: bold;
margin-bottom: 20px;
color: #198754;
text-align: center;
}
.compare-demo-area {
display: flex;
flex-wrap: wrap;
gap: 30px;
background-color: #e9ecef;
padding: 30px 20px;
border-radius: 8px;
border: 1px dashed #adb5bd;
margin-bottom: 20px;
justify-content: center;
}
.compare-box {
width: 100%;
max-width: 250px;
background-color: #ffffff;
padding: 15px;
border: 1px solid #ced4da;
border-radius: 4px;
text-align: center;
}
.compare-title {
margin-top: 0;
margin-bottom: 15px;
font-size: 13px;
font-weight: bold;
color: #0d6efd;
}
.card-list {
background-color: #f1f3f5;
padding: 10px;
border-radius: 4px;
margin-bottom: 15px;
display: flex;
flex-direction: column;
gap: 10px;
}
.card {
background-color: #6c757d;
color: #ffffff;
padding: 15px 10px;
border-radius: 4px;
font-size: 13px;
font-weight: bold;
}
.card:nth-child(2) {
background-color: #fd7e14;
}
.compare-desc {
margin: 0;
font-size: 11px;
color: #6c757d;
line-height: 1.5;
}
/* === 💡 比較1:visibility: hidden === */
.is-visibility:hover .target-vis {
/* 💡 姿は消えるが、スペースは残る */
visibility: hidden;
}
/* === 💡 比較2:display: none === */
.is-display:hover .target-disp {
/* 💡 存在が消え、下の要素が上に詰まる */
display: none;
}
/* =コード解説エリア(エディタ風)= */
.compare-code-area {
background-color: #282c34;
color: #abb2bf;
padding: 20px;
border-radius: 6px;
font-family: monospace;
font-size: 13px;
line-height: 1.6;
border-left: 4px solid #0d6efd;
overflow-x: auto;
text-align: left;
}
.hl-blue { color: #61afef; font-weight: bold; }
.hl-red { color: #e06c75; font-weight: bold; }
.hl-green { color: #98c379; font-weight: bold; }
.hl-comment { color: #6c757d; font-style: italic; }改めてdisplayについて詳しく知りたい人は「【CSS】display: blockとは?inlineやflexとの違いと種類」を一読ください。
もう一つ、visibilityとopacityの比較も実務で重要です。
opacity: 0;は要素の不透明度をゼロ(完全な透明)にするプロパティです。
見た目が消え、かつ領域が残るという点ではvisibility: hidden;と同じ挙動をします。
しかし、見えない「実体」がそこにあるかどうかで、決定的な違いが生じます。
アニメーションと非表示を両立させるには、フェードアウトさせる際はopacity: 0;とvisibility: hidden;をセットで記述し、両方にtransitionをかけることです。
これにより、『透明になりながら、同時にクリック判定も消し去る』という、安全で美しいアニメーションが実現できます。
⭕️ ホバーして比較!見えないのに押せてしまう「おばけボタン」の恐怖を体感しろ!
❌ 罠(opacity: 0のみ)
このテキストを選択・コピーしたいのに…
※透明になっても実体が残るため、
下のテキストが選択できず、
見えないボタンをクリックできてしまいます。
⭕️ 成功(opacity + visibility)
ボタンが消えた後は、
テキストが選択できます!
※透明になると同時に操作も無効化され、
下のテキストを安全に触ることができます。
<div class="opacity-vis-wrapper">
<p class="compare-display-caption">⭕️ ホバーして比較!見えないのに押せてしまう「おばけボタン」の恐怖を体感しろ!</p>
<div class="compare-demo-area">
<div class="compare-box is-opacity-only">
<p class="compare-title">❌ 罠(opacity: 0のみ)</p>
<div class="button-container">
<p class="under-text">このテキストを選択・コピーしたいのに…</p>
<button class="ghost-btn target-opacity" onclick="alert('見えないボタンが押されました!最悪のUXです!')">ホバーで透明になるボタン</button>
</div>
<p class="compare-desc">※透明になっても実体が残るため、<br>下のテキストが選択できず、<br>見えないボタンをクリックできてしまいます。</p>
</div>
<div class="compare-box is-perfect-fade">
<p class="compare-title">⭕️ 成功(opacity + visibility)</p>
<div class="button-container">
<p class="under-text">ボタンが消えた後は、<br>テキストが選択できます!</p>
<button class="ghost-btn target-perfect">ホバーで完全に消えるボタン</button>
</div>
<p class="compare-desc">※透明になると同時に操作も無効化され、<br>下のテキストを安全に触ることができます。</p>
</div>
</div>
<div class="compare-code-area">
<span class="hl-comment">/* ❌ 罠:opacity だけでフェードアウトさせると操作判定が残る */</span><br>
<span class="hl-blue">.box-fail:hover .target</span> {<br>
<span class="hl-green">opacity:</span> <span class="hl-red">0;</span> <span class="hl-comment">/* 🚨 見えない「壁」となり、下の要素の操作を妨害する */</span><br>
}<br><br>
<span class="hl-comment">/* ⭕️ フェードアウトは opacity と visibility を組み合わせる! */</span><br>
<span class="hl-blue">.target-perfect</span> {<br>
<span class="hl-green">opacity:</span> <span class="hl-red">1;</span><br>
<span class="hl-green">visibility:</span> <span class="hl-red">visible;</span><br>
<span class="hl-comment">/* 💡 両方にアニメーションをかけるのがコツ */</span><br>
<span class="hl-green">transition-property:</span> <span class="hl-red">opacity, visibility;</span><br>
<span class="hl-green">transition-duration:</span> <span class="hl-red">0.4s;</span><br>
<span class="hl-green">transition-timing-function:</span> <span class="hl-red">ease;</span><br>
}<br>
<span class="hl-blue">.box-success:hover .target-perfect</span> {<br>
<span class="hl-green">opacity:</span> <span class="hl-red">0;</span> <span class="hl-comment">/* 💡 フワッと透明になりつつ... */</span><br>
<span class="hl-green">visibility:</span> <span class="hl-red">hidden;</span> <span class="hl-comment">/* 💡 クリック判定も完全に消し去る! */</span><br>
}
</div>
</div>.opacity-vis-wrapper {
background-color: #f8f9fa;
padding: 30px;
border-radius: 8px;
border: 1px solid #dee2e6;
}
.button-container {
position: relative;
background-color: #f1f3f5;
height: 100px;
border-radius: 4px;
margin-bottom: 15px;
display: flex;
align-items: center;
justify-content: center;
padding: 10px;
}
.under-text {
margin: 0;
font-size: 12px;
color: #495057;
text-align: center;
}
.ghost-btn {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: #dc3545;
color: #fff;
border: none;
padding: 12px 15px;
border-radius: 4px;
font-weight: bold;
font-size: 12px;
cursor: pointer;
width: 90%;
}
/* === ❌ 罠:opacityのみ === */
.target-opacity {
/* 🚨 トランジションは効くが、透明になるだけ */
transition-property: opacity;
transition-duration: 0.4s;
transition-timing-function: ease;
}
.is-opacity-only:hover .target-opacity {
opacity: 0;
}
/* === ⭕️ 成功:opacity + visibility === */
.target-perfect {
background-color: #198754;
/* 💡 両方のプロパティを設定する */
transition-property: opacity, visibility;
transition-duration: 0.4s;
transition-timing-function: ease;
opacity: 1;
visibility: visible;
}
.is-perfect-fade:hover .target-perfect {
opacity: 0;
visibility: hidden; /* 💡 透明になると同時に操作も不能になる */
}
.compare-title,
.compare-desc {
text-align: center;
}
/* =コード解説エリア(エディタ風)= */
.compare-code-area {
background-color: #282c34;
color: #abb2bf;
padding: 20px;
border-radius: 6px;
font-family: monospace;
font-size: 13px;
line-height: 1.6;
border-left: 4px solid #0d6efd;
overflow-x: auto;
text-align: left;
}
.hl-blue { color: #61afef; font-weight: bold; }
.hl-red { color: #e06c75; font-weight: bold; }
.hl-green { color: #98c379; font-weight: bold; }
.hl-comment { color: #6c757d; font-style: italic; }透明度・透過を操るopacityの使い方を詳しく知りたい人は「【CSS】opacityの使い方:背景だけ透明にする方法とアニメーション」を一読ください。
visibilityプロパティの真骨頂は、CSSのtransition(変化のアニメーション)と組み合わせた時に発揮されます。
前章で「フェードアウトにはopacityとvisibilityを組み合わせる」と解説しましたが、ここではその仕組みであるtransitionの挙動を深く掘り下げます。
display: noneでは不可能な滑らかな表示切り替え要素がフワッと現れさてワッと消すといったシンプルで美しいUIは、display: none;を使っている限り実現できません。
displayは「0(無)か 1(有)か」しか状態を持たないため、中間の状態を描画できないからです。
そこで、opacity(0〜1の透明度)による滑らかな変化とvisibilityによる操作制御を掛け合わせるのが、現代のWebデザインにおけるフェードアニメーションの最適解となります。
滑らかな切り替えは、transition: opacity 0.5s ease, visibility 0.5s ease;のように、両方のプロパティをカンマ区切りでアニメーション対象に含めることです。
CSSのvisibilityは特別で、フェードアウト時は『opacityのアニメーションが終わるまでhiddenになるのを待ってくれる』という仕様を持つため、これを最大限活用することです。
⭕️ ホバーして比較!「visibility」にもtransitionをかけないと途中で消滅するぞ!
❌ 罠(opacityのみ指定)
※透明になる過程を見せずに
一瞬で消滅してしまいます。
⭕️ 成功(両方に指定)
※完全に透明になりきってから
hidden状態に移行します。
<div class="fade-trans-wrapper">
<p class="fade-trans-caption">⭕️ ホバーして比較!「visibility」にもtransitionをかけないと途中で消滅するぞ!</p>
<div class="fade-demo-area">
<div class="fade-box is-trap-notrans">
<p class="fade-title">❌ 罠(opacityのみ指定)</p>
<div class="fade-target">ホバーで消えます</div>
<p class="fade-desc">※透明になる過程を見せずに<br>一瞬で消滅してしまいます。</p>
</div>
<div class="fade-box is-success-both">
<p class="fade-title">⭕️ 成功(両方に指定)</p>
<div class="fade-target">ホバーで消えます</div>
<p class="fade-desc">※完全に透明になりきってから<br>hidden状態に移行します。</p>
</div>
</div>
<div class="fade-code-area">
<span class="hl-comment">/* ❌ 罠:opacityだけにtransitionをかけている */</span><br>
<span class="hl-blue">.box-fail .target</span> {<br>
<span class="hl-green">transition-property:</span> <span class="hl-red">opacity;</span> <span class="hl-comment">/* 🚨 これが原因 */</span><br>
<span class="hl-green">transition-duration:</span> <span class="hl-red">1s;</span><br>
}<br>
<span class="hl-blue">.box-fail:hover .target</span> {<br>
<span class="hl-green">opacity:</span> <span class="hl-red">0;</span><br>
<span class="hl-green">visibility:</span> <span class="hl-red">hidden;</span> <span class="hl-comment">/* 待ってくれないので一瞬で消える! */</span><br>
}<br><br>
<span class="hl-comment">/* ⭕️ visibilityも一緒にtransitionに登録する! */</span><br>
<span class="hl-blue">.box-success .target</span> {<br>
<span class="hl-green">transition-property:</span> <span class="hl-red">opacity, visibility;</span> <span class="hl-comment">/* 💡 両方指定する! */</span><br>
<span class="hl-green">transition-duration:</span> <span class="hl-red">1s;</span><br>
}<br>
<span class="hl-blue">.box-success:hover .target</span> {<br>
<span class="hl-green">opacity:</span> <span class="hl-red">0;</span><br>
<span class="hl-green">visibility:</span> <span class="hl-red">hidden;</span> <span class="hl-comment">/* 💡 opacityが0になるまで待ってからhiddenになる魔法の挙動! */</span><br>
}
</div>
</div>.fade-trans-wrapper {
background-color: #f8f9fa;
padding: 30px;
border-radius: 8px;
border: 1px solid #dee2e6;
}
.fade-trans-caption {
font-size: 14px;
font-weight: bold;
margin-bottom: 20px;
color: #198754;
text-align: center;
}
.fade-demo-area {
display: flex;
flex-wrap: wrap;
gap: 30px;
background-color: #e9ecef;
padding: 30px 20px;
border-radius: 8px;
border: 1px dashed #adb5bd;
margin-bottom: 20px;
justify-content: center;
}
.fade-box {
width: 100%;
max-width: 250px;
background-color: #ffffff;
padding: 15px;
border: 1px solid #ced4da;
border-radius: 4px;
text-align: center;
cursor: pointer;
}
.fade-title {
margin-top: 0;
margin-bottom: 15px;
font-size: 13px;
font-weight: bold;
}
.is-trap-notrans .fade-title { color: #dc3545; }
.is-success-both .fade-title { color: #198754; }
.fade-target {
background-color: #0d6efd;
color: #fff;
padding: 20px;
border-radius: 4px;
font-weight: bold;
margin-bottom: 15px;
opacity: 1;
visibility: visible;
}
.fade-desc {
margin: 0;
font-size: 11px;
color: #6c757d;
line-height: 1.5;
}
/* === ❌ 罠:opacityのみのtransition === */
.is-trap-notrans .fade-target {
/* 🚨 visibilityへの指定が漏れている */
transition-property: opacity;
transition-duration: 1s;
transition-timing-function: ease;
}
.is-trap-notrans:hover .fade-target {
opacity: 0;
visibility: hidden; /* アニメーションしないので一瞬で消える */
}
/* === ⭕️ 成功:両方のtransition === */
.is-success-both .fade-target {
/* 💡 完璧なフェードアウトの設定 */
transition-property: opacity, visibility;
transition-duration: 1s;
transition-timing-function: ease;
}
.is-success-both:hover .fade-target {
opacity: 0;
visibility: hidden; /* opacityが0になるまで待機してくれる */
}
/* =コード解説エリア(エディタ風)= */
.fade-code-area {
background-color: #282c34;
color: #abb2bf;
padding: 20px;
border-radius: 6px;
font-family: monospace;
font-size: 13px;
line-height: 1.6;
border-left: 4px solid #0d6efd;
overflow-x: auto;
text-align: left;
}
.hl-blue { color: #61afef; font-weight: bold; }
.hl-red { color: #e06c75; font-weight: bold; }
.hl-green { color: #98c379; font-weight: bold; }
.hl-comment { color: #6c757d; font-style: italic; }アニメーションの軸になるtransitionの使い方を詳しく知りたい人は「【CSS】transitionの書き方!transformとの違いやコピペ用コード集」を一読ください。
「クリックしたら開き、もう一度クリックしたら閉じる」というドロップダウンメニューやアコーディオンUIがあります。
通常はJavaScriptを使ってclassを付け外ししますが、CSSの機能だけでこれを実現する有名な裏技が存在します。
それが「チェックボックスハック(Checkbox Hack)」と呼ばれる手法です。
<input type="checkbox">と<label>を組み合わせ、CSSの:checked疑似クラスと兄弟セレクタ(~または+)をフックにしてvisibilityを切り替えることで、JS不要のトグルを実装できます。
JSなしトグルは、「①<input type="checkbox">はdisplay: none;で隠すこと。②デザインされたボタンは<label for="ID名">を使って作成し、クリック判定をチェックボックスに飛ばすこと。③隠されたinputと表示させたいtarget要素はHTML上で同じ階層に並べて配置すること」です。
⭕️ クリックして確認!JSなしで「開閉(トグル)」させるチェックボックスハック!
🎉 CSSだけで開きました!
JavaScriptは一切使っていません。
<div class="toggle-hack-wrapper">
<p class="fade-trans-caption">⭕️ クリックして確認!JSなしで「開閉(トグル)」させるチェックボックスハック!</p>
<div class="toggle-demo-area">
<div class="toggle-container">
<input type="checkbox" id="toggle-menu" class="hidden-checkbox">
<label for="toggle-menu" class="toggle-btn">
メニューを開閉する
</label>
<div class="toggle-target">
<p>🎉 CSSだけで開きました!</p>
<p>JavaScriptは一切使っていません。</p>
</div>
</div>
</div>
<div class="fade-code-area">
<span class="hl-comment">/* 💡 初期状態:ターゲットは見えなくしておく */</span><br>
<span class="hl-blue">.toggle-target</span> {<br>
<span class="hl-green">opacity:</span> <span class="hl-red">0;</span><br>
<span class="hl-green">visibility:</span> <span class="hl-red">hidden;</span><br>
<span class="hl-green">transform:</span> <span class="hl-red">translateY(-10px);</span><br>
<span class="hl-green">transition-property:</span> <span class="hl-red">opacity, visibility, transform;</span><br>
<span class="hl-green">transition-duration:</span> <span class="hl-red">0.3s;</span><br>
}<br><br>
<span class="hl-comment">/* ⭕️ チェックボックスが :checked になった時、弟(~)を表示する! */</span><br>
<span class="hl-blue">.hidden-checkbox:checked ~ .toggle-target</span> {<br>
<span class="hl-green">opacity:</span> <span class="hl-red">1;</span><br>
<span class="hl-green">visibility:</span> <span class="hl-red">visible;</span> <span class="hl-comment">/* 💡 ここで表示させる! */</span><br>
<span class="hl-green">transform:</span> <span class="hl-red">translateY(0);</span><br>
}
</div>
</div>.toggle-hack-wrapper {
background-color: #f8f9fa;
padding: 30px;
border-radius: 8px;
border: 1px solid #dee2e6;
}
.toggle-demo-area {
display: flex;
justify-content: center;
background-color: #e9ecef;
padding: 40px 20px 80px 20px; /* メニューが開く分の余白を下にとる */
border-radius: 8px;
border: 1px dashed #adb5bd;
margin-bottom: 20px;
}
.toggle-container {
position: relative;
width: 100%;
max-width: 300px;
display: flex;
flex-direction: column;
align-items: center;
}
/* === 💡 1. 隠しチェックボックス === */
.hidden-checkbox {
/* 実務では display: none; にするが、今回はデモのため薄く残す */
position: absolute;
top: -20px;
opacity: 0.3;
cursor: pointer;
}
/* === 💡 2. ボタン(label) === */
.toggle-btn {
background-color: #6f42c1;
color: #ffffff;
padding: 12px 20px;
border-radius: 6px;
font-weight: bold;
cursor: pointer;
user-select: none; /* テキスト選択を防ぐ */
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
transition: background-color 0.2s;
z-index: 2;
position: relative;
}
.toggle-btn:hover {
background-color: #5936a2;
}
/* === 💡 3. 切り替わるターゲット === */
.toggle-target {
background-color: #ffffff;
border: 1px solid #ced4da;
border-radius: 6px;
padding: 15px;
width: 100%;
box-sizing: border-box;
text-align: center;
margin-top: 10px;
box-shadow: 0 10px 15px rgba(0,0,0,0.1);
position: absolute;
top: 100%;
/* 💡 初期状態(非表示)とアニメーション設定 */
opacity: 0;
visibility: hidden;
transform: translateY(-10px);
transition-property: opacity, visibility, transform;
transition-duration: 0.3s;
transition-timing-function: ease;
z-index: 1;
}
.toggle-target p {
margin: 5px 0;
font-size: 13px;
color: #333;
}
/* === ⭕️ :checked と 兄弟セレクタ(~) === */
.hidden-checkbox:checked ~ .toggle-target {
opacity: 1;
visibility: visible;
transform: translateY(0);
}
/* おまけ:チェックされたらボタンの色も変える */
.hidden-checkbox:checked ~ .toggle-btn {
background-color: #198754;
}
/* =コード解説エリア(エディタ風)= */
.fade-code-area {
background-color: #282c34;
color: #abb2bf;
padding: 20px;
border-radius: 6px;
font-family: monospace;
font-size: 13px;
line-height: 1.6;
border-left: 4px solid #0d6efd;
overflow-x: auto;
text-align: left;
}
.hl-blue { color: #61afef; font-weight: bold; }
.hl-red { color: #e06c75; font-weight: bold; }
.hl-green { color: #98c379; font-weight: bold; }
.hl-comment { color: #6c757d; font-style: italic; }inputタグ・labelタグの使い方を詳しく知りたい人は以下から一読ください。
CSSの:hoverや:checkedを使えば、ある程度の表示・非表示の切り替えは可能です。
しかし、「特定のボタンをクリックした時」「画面をある程度スクロールした時」「フォームのエラーチェックに引っかかった時」など、より複雑で動的なタイミングで要素を出し入れするには、JavaScriptとの連携が不可欠になります。
ここでは、CSSのvisibilityプロパティをJavaScriptから安全に操作するモダンな実装手法と保守性を下げてしまうNGな書き方について解説します。
visibilityを変更する方法ボタンのクリックイベントなどをトリガーにして要素を隠す場合、現代のWeb開発では素のJavaScriptを使うのが標準的ですが、既存のプロジェクトではjQueryを活用する場面もまだ存在します。
実装する際、どのような要素を取得するかと取得した要素に対してどうやってスタイルを適用するかが重要になります。
element.style.visibility = 'hidden';$('.target').css('visibility', 'hidden');これを実行すると、HTMLの要素に直接style="visibility: hidden;"というインラインスタイルが書き込まれてしまいます。
インラインスタイルはCSSの優先度(詳細度)において最強であるため、後からレスポンシブ対応などで「スマホの時はCSSで表示させたい」と思っても、JSが書き込んだインラインスタイルが邪魔をしてCSSが効かなくなるという深刻なバグを引き起こします。
JSで表示・非表示を切り替えるには、JavaScript側ではCSSのプロパティを直接操作せず、『クラス(例:.is-hidden)の付け外し』のみに徹することです。
そして、実際の表示・非表示の切り替えやアニメーションの挙動はCSS側に委ねることです。
これにより、デザイン(CSS)とロジック(JS)が分離され、バグの起きにくい堅牢なコードになります。
⭕️ クリックして確認!JSは「クラスの付け外し」だけを行い、見た目はCSSに任せろ!
⚠️ システムにエラーが発生しました。
(この要素のクラスがJSで切り替わります)
<div class="js-vis-wrapper">
<p class="js-vis-caption">⭕️ クリックして確認!JSは「クラスの付け外し」だけを行い、見た目はCSSに任せろ!</p>
<div class="js-demo-area">
<div class="js-demo-box">
<button id="js-toggle-btn" class="action-btn">
アラートを切り替える (Toggle)
</button>
<div id="js-target-alert" class="alert-box">
<p>⚠️ システムにエラーが発生しました。</p>
<p>(この要素のクラスがJSで切り替わります)</p>
</div>
</div>
</div>
<div class="js-code-area">
<span class="hl-comment">/* === CSS側 === */</span><br>
<span class="hl-comment">/* 💡 通常時のスタイル(表示状態) */</span><br>
<span class="hl-blue">.alert-box</span> {<br>
<span class="hl-green">opacity:</span> <span class="hl-red">1;</span><br>
<span class="hl-green">visibility:</span> <span class="hl-red">visible;</span><br>
<span class="hl-green">transition-property:</span> <span class="hl-red">opacity, visibility;</span><br>
<span class="hl-green">transition-duration:</span> <span class="hl-red">0.3s;</span><br>
}<br><br>
<span class="hl-comment">/* ⭕️ JSで付与するための「隠す専用クラス」を用意する */</span><br>
<span class="hl-blue">.alert-box.is-hidden</span> {<br>
<span class="hl-green">opacity:</span> <span class="hl-red">0;</span><br>
<span class="hl-green">visibility:</span> <span class="hl-red">hidden;</span> <span class="hl-comment">/* 💡 ここで確実に非アクティブ化 */</span><br>
}<br><br>
<span class="hl-comment">/* === JavaScript側 === */</span><br>
<span class="hl-blue">const</span> btn = document.<span class="hl-green">getElementById</span>(<span class="hl-red">'js-toggle-btn'</span>);<br>
<span class="hl-blue">const</span> target = document.<span class="hl-green">getElementById</span>(<span class="hl-red">'js-target-alert'</span>);<br><br>
<span class="hl-comment">/* 💡 ボタンがクリックされたら */</span><br>
btn.<span class="hl-green">addEventListener</span>(<span class="hl-red">'click'</span>, () => {<br>
<span class="hl-comment">/* ❌ 罠: target.style.visibility = 'hidden'; とは絶対に書かない! */</span><br>
<span class="hl-comment">/* ⭕️ 成功:対象要素に 'is-hidden' クラスをトグル(無ければ付け、有れば外す)する */</span><br>
target.classList.<span class="hl-green">toggle</span>(<span class="hl-red">'is-hidden'</span>);<br>
});<br><br>
<span class="hl-comment">/* 💡 参考:jQueryで書く場合(既存プロジェクト向け) */</span><br>
<span class="hl-comment">/* $('#js-toggle-btn').on('click', function() { */</span><br>
<span class="hl-comment">/* $('#js-target-alert').toggleClass('is-hidden'); */</span><br>
<span class="hl-comment">/* }); */</span>
</div>
</div>.js-vis-wrapper {
background-color: #f8f9fa;
padding: 30px;
border-radius: 8px;
border: 1px solid #dee2e6;
}
.js-vis-caption {
font-size: 14px;
font-weight: bold;
margin-bottom: 20px;
color: #198754;
text-align: center;
}
.js-demo-area {
display: flex;
justify-content: center;
background-color: #e9ecef;
padding: 40px 20px;
border-radius: 8px;
border: 1px dashed #adb5bd;
margin-bottom: 20px;
}
.js-demo-box {
width: 100%;
max-width: 400px;
display: flex;
flex-direction: column;
align-items: center;
}
.action-btn {
background-color: #0d6efd;
color: #ffffff;
padding: 12px 20px;
border: none;
border-radius: 6px;
font-weight: bold;
font-size: 14px;
cursor: pointer;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
transition-property: background-color;
transition-duration: 0.2s;
margin-bottom: 20px;
}
.action-btn:hover {
background-color: #0b5ed7;
}
/* === 💡 CSS側:切り替えアニメーションと初期状態 === */
.alert-box {
background-color: #f8d7da;
color: #842029;
border: 1px solid #f5c2c7;
border-radius: 6px;
padding: 15px;
width: 100%;
box-sizing: border-box;
text-align: center;
/* 初期状態(表示)とアニメーションの設定 */
opacity: 1;
visibility: visible;
transition-property: opacity, visibility;
transition-duration: 0.3s;
transition-timing-function: ease;
}
.alert-box p {
margin: 5px 0;
font-size: 13px;
}
/* === ⭕️ JSで付与する隠しクラス === */
.alert-box.is-hidden {
/* JSによってこのクラスが付与されると、CSSの力でフェードアウトする */
opacity: 0;
visibility: hidden;
}
/* =コード解説エリア(エディタ風)= */
.js-code-area {
background-color: #282c34;
color: #abb2bf;
padding: 20px;
border-radius: 6px;
font-family: monospace;
font-size: 13px;
line-height: 1.6;
border-left: 4px solid #0d6efd;
overflow-x: auto;
text-align: left;
}
.hl-blue { color: #61afef; font-weight: bold; }
.hl-red { color: #e06c75; font-weight: bold; }
.hl-green { color: #98c379; font-weight: bold; }
.hl-comment { color: #6c757d; font-style: italic; }CSSのvisibilityは、「要素を隠す・表示する」という基本的な役割の他に、パフォーマンス最適化や3Dアニメーション、テーブルレイアウトに特化した特殊な仲間や値が存在します。
ここでは、モダンWeb開発で武器となるcontent-visibilityやカードの裏返し実装に必須のbackface-visibility、テーブルレイアウトで威力を発揮するvisibility: collapse;といった応用テクニックを解説します。
また、visibilityが効かないと悩んだ時に役立つトラブルシューティングのチェックリストも用意しました。
content-visibility: autobackface-visibilityvisibility: collapsevisibilityが効かない時のチェックリスト近年追加された強力なプロパティがcontent-visibilityです。
特にcontent-visibility: autoを指定すると、要素が画面外(スクロールして見えない位置)にある間、ブラウザは中身のレンダリング(描画計算)をスキップします。
これにより、画像や複雑なDOMが多い縦長のページの初期表示スピードが爆速に向上します。
描画スキップを導入するには、content-visibility: auto;を使う際は、contain-intrinsic-sizeプロパティをセットで記述し、描画される前の『仮の高さ(プレースホルダーサイズ)』をブラウザに教えてあげることです。
これにより、スクロールバーの暴れを防ぐことができます。
⭕️ 爆速化の代償「スクロールのガタつき」は、仮の高さを与えて防げ!
最適化された重いコンテンツ 1
画面外にある間、この中身の描画はスキップされます。
最適化された重いコンテンツ 2
contain-intrinsic-size のおかげで、
描画前でも仮の高さが確保されています。
最適化された重いコンテンツ 3
スクロールして画面に入った瞬間に、
高速でレンダリングが実行されます。
<div class="cv-wrapper">
<p class="cv-caption">⭕️ 爆速化の代償「スクロールのガタつき」は、仮の高さを与えて防げ!</p>
<div class="cv-demo-area">
<div class="cv-scroll-box">
<div class="cv-section is-optimized">
<p class="cv-title">最適化された重いコンテンツ 1</p>
<p>画面外にある間、この中身の描画はスキップされます。</p>
</div>
<div class="cv-section is-optimized">
<p class="cv-title">最適化された重いコンテンツ 2</p>
<p>contain-intrinsic-size のおかげで、<br>描画前でも仮の高さが確保されています。</p>
</div>
<div class="cv-section is-optimized">
<p class="cv-title">最適化された重いコンテンツ 3</p>
<p>スクロールして画面に入った瞬間に、<br>高速でレンダリングが実行されます。</p>
</div>
</div>
</div>
<div class="cv-code-area">
<span class="hl-comment">/* ⭕️ auto と contain-intrinsic-size は必ずセット! */</span><br>
<span class="hl-blue">.is-optimized</span> {<br>
<span class="hl-comment">/* 💡 画面外のレンダリングをスキップし、パフォーマンスを向上させる */</span><br>
<span class="hl-green">content-visibility:</span> <span class="hl-red">auto;</span><br>
<br>
<span class="hl-comment">/* 💡 必須:スキップ中の「仮の高さ」を指定し、スクロールのガタつきを防ぐ */</span><br>
<span class="hl-green">contain-intrinsic-size:</span> <span class="hl-red">auto 200px;</span><br>
}
</div>
</div>.cv-wrapper { background-color: #f8f9fa; padding: 30px; border-radius: 8px; border: 1px solid #dee2e6; }
.cv-caption { font-size: 14px; font-weight: bold; margin-bottom: 20px; color: #198754; text-align: center; }
.cv-demo-area { background-color: #e9ecef; padding: 20px; border-radius: 8px; margin-bottom: 20px; }
/* === デモ用スクロール領域 === */
.cv-scroll-box {
height: 250px;
overflow-y: auto;
background-color: #fff;
border: 1px solid #ced4da;
padding: 15px;
}
.cv-section {
background-color: #f1f3f5;
border-left: 4px solid #0d6efd;
padding: 20px;
margin-bottom: 20px;
min-height: 160px; /* 実際のコンテンツの高さの想定 */
}
.cv-title { font-weight: bold; color: #0d6efd; margin-top: 0; }
/* === 💡 最適化指定 === */
.is-optimized {
content-visibility: auto;
/* 描画前の仮の高さ(実際の高さに近い値を設定する) */
contain-intrinsic-size: auto 160px;
}
/* =コード解説エリア(エディタ風)= */
.cv-code-area { background-color: #282c34; color: #abb2bf; padding: 20px; border-radius: 6px; font-family: monospace; font-size: 13px; line-height: 1.6; border-left: 4px solid #0d6efd; overflow-x: auto; }
.hl-blue { color: #61afef; font-weight: bold; }
.hl-red { color: #e06c75; font-weight: bold; }
.hl-green { color: #98c379; font-weight: bold; }
.hl-comment { color: #6c757d; font-style: italic; }ホバーするとクルッと回転するカードUIがあります。
3Dアニメーションを実装する際に欠かせないのがbackface-visibilityプロパティです。
要素がY軸(またはX軸)に180度回転して「裏側」を向いた際、要素の中身を透かして見せるか、隠すかを制御します。
カード反転を実装するには、「親要素には3D空間を維持するtransform-style: preserve-3d;を指定し、backface-visibility: hidden;は実際に回転する『表面の子要素』と『裏面の子要素』の両方に直接指定すること」です。
⭕️ ホバーして反転!「backface-visibility」は表面と裏面の両方に指定しろ!
表面 (Front)
ホバーで裏返ります裏面 (Back)
表面の透けは hidden で隠しています<div class="backface-wrapper">
<p class="cv-caption">⭕️ ホバーして反転!「backface-visibility」は表面と裏面の両方に指定しろ!</p>
<div class="backface-demo-area">
<div class="flip-card">
<div class="flip-card-inner">
<div class="flip-card-front">
<p>表面 (Front)</p>
<span class="small">ホバーで裏返ります</span>
</div>
<div class="flip-card-back">
<p>裏面 (Back)</p>
<span class="small">表面の透けは hidden で隠しています</span>
</div>
</div>
</div>
</div>
<div class="cv-code-area">
<span class="hl-comment">/* 💡 親要素:3D空間の維持とアニメーション設定 */</span><br>
<span class="hl-blue">.flip-card-inner</span> {<br>
<span class="hl-green">transition:</span> <span class="hl-red">transform 0.6s;</span><br>
<span class="hl-green">transform-style:</span> <span class="hl-red">preserve-3d;</span> <span class="hl-comment">/* 必須:子要素の3D配置を許可 */</span><br>
}<br><br>
<span class="hl-comment">/* ⭕️ 表面と裏面の両方に指定して透けを防ぐ */</span><br>
<span class="hl-blue">.flip-card-front, .flip-card-back</span> {<br>
<span class="hl-green">position:</span> <span class="hl-red">absolute;</span><br>
<span class="hl-green">backface-visibility:</span> <span class="hl-red">hidden;</span> <span class="hl-comment">/* 💡 要素が裏を向いた時に隠す魔法のプロパティ */</span><br>
}<br><br>
<span class="hl-comment">/* 💡 裏面はあらかじめ180度回しておく */</span><br>
<span class="hl-blue">.flip-card-back</span> {<br>
<span class="hl-green">transform:</span> <span class="hl-red">rotateY(180deg);</span><br>
}
</div>
</div>.backface-wrapper { background-color: #f8f9fa; padding: 30px; border-radius: 8px; border: 1px solid #dee2e6; }
.backface-demo-area { display: flex; justify-content: center; background-color: #e9ecef; padding: 40px; border-radius: 8px; margin-bottom: 20px; perspective: 1000px; }
/* === 💡 3DフリップカードのCSS === */
.flip-card {
background-color: transparent;
width: 200px;
height: 200px;
perspective: 1000px; /* 奥行きの視点 */
}
.flip-card-inner {
position: relative;
width: 100%;
height: 100%;
text-align: center;
transition: transform 0.6s;
/* 💡 子要素を3D空間に配置 */
transform-style: preserve-3d;
}
/* ホバー時にY軸で180度回転 */
.flip-card:hover .flip-card-inner {
transform: rotateY(180deg);
}
/* 表面と裏面の共通スタイル */
.flip-card-front, .flip-card-back {
position: absolute;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
border-radius: 8px;
font-weight: bold;
/* ⭕️ 裏返った時に中身を隠す */
backface-visibility: hidden;
}
.flip-card-front {
background-color: #0d6efd;
color: white;
}
.flip-card-back {
background-color: #198754;
color: white;
/* 裏面はあらかじめ180度回しておく */
transform: rotateY(180deg);
}
.flip-card .small { font-size: 11px; font-weight: normal; margin-top: 10px; }
/* =コード解説エリア(エディタ風)= */
.cv-code-area { background-color: #282c34; color: #abb2bf; padding: 20px; border-radius: 6px; font-family: monospace; font-size: 13px; line-height: 1.6; border-left: 4px solid #0d6efd; overflow-x: auto; }
.hl-blue { color: #61afef; font-weight: bold; }
.hl-red { color: #e06c75; font-weight: bold; }
.hl-green { color: #98c379; font-weight: bold; }
.hl-comment { color: #6c757d; font-style: italic; }transformの使い方を詳しく知りたい人は「【CSS】transformの使い方:移動・回転・拡大と効かない時の対策」を一読ください。
通常、要素の領域を詰めて消す場合はdisplay: none;を使いますが、テーブル(table)のレイアウトにおいてこれをやってしまうと、列の幅(セル幅)の計算が狂い、レイアウトがガタガタに崩れることがあります。
そこで用意されているのが、テーブル要素(trやcolなど)専用のプロパティであるvisibility: collapse;です。
これを指定すると、テーブルの列幅などを維持したまま、指定した行だけを「スペースごと詰めて隠す」ことができます。
visibility: collapse;は、テーブルの行(tr)や列(col)を、列幅のレイアウトを維持したまま動的に開閉させたい時にのみ使用する、テーブル専用の技であると認識することです。
⭕️ テーブルの行を隠すなら、列幅を守る「collapse」を使え!
| ID | 商品名(幅が広い列) | ステータス |
|---|---|---|
| 001 | プレミアム・コーヒー豆 | 表示中 |
| 002 | ※※※ この行はcollapseで非表示 ※※※ | 隠れる |
| 003 | 通常ブレンド | 表示中 |
<div class="collapse-wrapper">
<p class="cv-caption">⭕️ テーブルの行を隠すなら、列幅を守る「collapse」を使え!</p>
<div class="collapse-demo-area">
<table class="demo-collapse-table">
<thead>
<tr>
<th>ID</th>
<th>商品名(幅が広い列)</th>
<th>ステータス</th>
</tr>
</thead>
<tbody>
<tr>
<td>001</td>
<td>プレミアム・コーヒー豆</td>
<td>表示中</td>
</tr>
<tr class="row-collapse">
<td>002</td>
<td>※※※ この行はcollapseで非表示 ※※※</td>
<td>隠れる</td>
</tr>
<tr>
<td>003</td>
<td>通常ブレンド</td>
<td>表示中</td>
</tr>
</tbody>
</table>
</div>
<div class="cv-code-area">
<span class="hl-comment">/* ⭕️ テーブルの tr や col に対してのみ真価を発揮する */</span><br>
<span class="hl-blue">.row-collapse</span> {<br>
<span class="hl-green">visibility:</span> <span class="hl-red">collapse;</span> <span class="hl-comment">/* 💡 display:noneと違い、テーブルの列幅計算を狂わせずに間を詰める! */</span><br>
}
</div>
</div>.collapse-wrapper { background-color: #f8f9fa; padding: 30px; border-radius: 8px; border: 1px solid #dee2e6; margin-bottom: 30px; }
.collapse-demo-area { background-color: #ffffff; padding: 20px; border: 1px dashed #adb5bd; border-radius: 8px; margin-bottom: 20px; overflow-x: auto; }
/* === 💡 collapse検証用テーブル === */
.demo-collapse-table {
width: 100%;
border-collapse: collapse;
}
.demo-collapse-table th, .demo-collapse-table td {
border: 1px solid #ced4da;
padding: 12px;
text-align: center;
font-size: 14px;
}
.demo-collapse-table th { background-color: #e9ecef; }
/* 💡 行を詰めて隠す設定 */
.row-collapse {
visibility: collapse;
/* ここが none だと、2行目の長いテキストの幅情報が失われ、
テーブル全体の幅バランスが変わる可能性がある */
}
/* =コード解説エリア(エディタ風)= */
.cv-code-area { background-color: #282c34; color: #abb2bf; padding: 20px; border-radius: 6px; font-family: monospace; font-size: 13px; line-height: 1.6; border-left: 4px solid #0d6efd; overflow-x: auto; }
.hl-blue { color: #61afef; font-weight: bold; }
.hl-red { color: #e06c75; font-weight: bold; }
.hl-green { color: #98c379; font-weight: bold; }
.hl-comment { color: #6c757d; font-style: italic; }tableタグの使い方あるいはテーブルの作り方を詳しく知りたい人は「HTMLのtable(テーブル)タグの使い方!複雑なセル結合とレスポンシブ」を一読ください。
最後に、実務でvisiilityが効かないと悩んだ際に確認すべき、ありがちな盲点とチェックリストをまとめます。
一つ目の注意点は、親要素にvisibility: hidden;を指定したのに、中のテキストや画像が消えないケースです。
visibilityは子要素に遺伝(継承)しますが、もし子要素に明示的にvisibility: visible;が指定されていた場合、親が隠れていても子要素だけは堂々と画面に表示されます。
これはdisplay: noneにはない特殊な仕様です。
意図せず子要素が上書き設定されていないか、検証ツールで確認してください。
二つ目の注意点は、初心者が存在しないプロパティを探してしまうケースです。
visibilityは「要素全体」を隠すプロパティです。
背景色だけ、あるいは枠線だけを隠したい場合はvisibilityは使えません。
背景や枠線だけを非表示にするにはvisibilityではなく、色を透明にする指定を行うことです。
背景ならbackground-color: transparent;、枠線ならborder-color: transparent;を指定すれば、中のテキストは表示したまま、背景や枠線だけを消し去ることができます。
⭕️ 親を hidden にしても、子が visible なら子は消えない!
<div class="trouble-wrapper">
<p class="cv-caption">⭕️ 親を hidden にしても、子が visible なら子は消えない!</p>
<div class="trouble-demo-area">
<div class="trouble-box is-parent-hidden">
親要素 (hidden)
<div class="trouble-child is-child-visible">
❌ 罠:子要素だけ表示される
</div>
</div>
<div class="trouble-box is-bg-transparent">
<div class="trouble-child">
💡 背景や枠線を消すなら transparent を使う
</div>
</div>
</div>
<div class="cv-code-area">
<span class="hl-comment">/* ❌ 罠:親のhiddenを、子要素が打ち消してしまうケース */</span><br>
<span class="hl-blue">.parent</span> {<br>
<span class="hl-green">visibility:</span> <span class="hl-red">hidden;</span><br>
}<br>
<span class="hl-blue">.child</span> {<br>
<span class="hl-green">visibility:</span> <span class="hl-red">visible;</span> <span class="hl-comment">/* 🚨 親がhiddenでも、子は表示されてしまう特殊仕様! */</span><br>
}<br><br>
<span class="hl-comment">/* ⭕️ 背景や枠線だけを隠すなら色を transparent にする */</span><br>
<span class="hl-blue">.box-transparent</span> {<br>
<span class="hl-green">background-color:</span> <span class="hl-red">transparent;</span> <span class="hl-comment">/* 💡 背景だけ消える */</span><br>
<span class="hl-green">border-color:</span> <span class="hl-red">transparent;</span> <span class="hl-comment">/* 💡 枠線だけ消える */</span><br>
}
</div>
</div>.trouble-wrapper { background-color: #f8f9fa; padding: 30px; border-radius: 8px; border: 1px solid #dee2e6; }
.trouble-demo-area { display: flex; flex-wrap: wrap; gap: 30px; justify-content: center; background-color: #e9ecef; padding: 30px 20px; border-radius: 8px; margin-bottom: 20px; }
.trouble-box {
width: 100%;
max-width: 250px;
background-color: #f8d7da;
border: 2px dashed #dc3545;
padding: 20px;
border-radius: 4px;
text-align: center;
font-weight: bold;
color: #842029;
}
.trouble-child {
background-color: #fff;
color: #333;
padding: 10px;
margin-top: 15px;
border: 1px solid #ccc;
font-size: 13px;
}
/* === ❌ 罠:子のオーバーライド === */
.is-parent-hidden {
/* 親を隠す */
visibility: hidden;
}
.is-child-visible {
/* 🚨 子がオーバーライドして表示されてしまう */
visibility: visible;
border: 2px solid #dc3545;
color: #dc3545;
}
/* === 💡 解決:背景の透過 === */
.is-bg-transparent {
/* 透明を指定する */
background-color: transparent;
border-color: transparent;
/* 文字や子要素は普通に表示される */
color: #198754;
}
.is-bg-transparent .trouble-child {
border-color: #198754;
color: #198754;
}
/* =コード解説エリア(エディタ風)= */
.cv-code-area { background-color: #282c34; color: #abb2bf; padding: 20px; border-radius: 6px; font-family: monospace; font-size: 13px; line-height: 1.6; border-left: 4px solid #0d6efd; overflow-x: auto; }
.hl-blue { color: #61afef; font-weight: bold; }
.hl-red { color: #e06c75; font-weight: bold; }
.hl-green { color: #98c379; font-weight: bold; }
.hl-comment { color: #6c757d; font-style: italic; }分かりやすいようにまとめを記載します。
visibility: hiddenは視覚的に消えるだけでなく、クリック等の操作判定も無効化する。display: noneは要素の領域ごと消滅し、他の要素が詰められてレイアウトが変わる。opacityとvisibilityの両方にtransitionを指定する。.is-hidden)を付け外してCSS側で制御する。contain-intrinsic-sizeでの仮高さ指定が必須)。tr)や列(col)専用。最大の違いは「要素があったスペース(領域)が残るかどうか」です。
visibility: hidden;は、透明マントを被ったように姿が消えるだけで、その要素が占めていたスペースは空白としてそのまま残ります。
一方、display: none;は要素そのものがHTMLから無かったこととして扱われるため、空いたスペースを埋めるように後ろの要素が上に詰まります。
見た目が消えてスペースが残るという点は同じですが、「クリック等の操作判定が残るかどうか」が異なります。
opacity: 0;は完全に透明になっているだけで実体はそこにあるため、ユーザーが見えないボタンを誤ってクリックしてしまう危険性があります。
visibility: hidden;を使えば、見た目を消すと同時にクリック判定やテキスト選択も完全に無効化できるため安全です。
中にある子要素に対して、CSSで明示的にvisibility: visible;が指定されていないか確認してください。
visibilityプロパティには、親要素が非表示設定であっても、子要素に「表示(visible)」が指定されていれば、その子要素だけは堂々と画面に表示されるという特殊な仕様があります。
これはdisplay: none;にはない特徴です。
visibilityプロパティ単体では作れませんが、opacityと組み合わせることで可能になります。
opacity: 0;(透明度)とvisibility: hidden;の両方にtransition(アニメーション)を設定してください。
「フワッと透明になり、完全に透明になった瞬間にhidden状態になって操作も無効化される」という、安全なフェードアウトが実装できます。
いいえ、できません。
visibilityは「要素全体」を丸ごと隠してしまうプロパティです。
テキストを残して背景や枠線だけを消したい場合は、visibilityを使うのではなくbackground-color: transparent;やborder-color: transparent;を使用してください。
サイト制作でお困りの人はお気軽にご連絡ください。
どんなお悩み事も丁寧に返信させて頂きます。
「どのサーバーを選べばいいか分からない…」そんな悩みを解決!
WordPressデビューに最適なサーバーを徹底比較しました。
