# CSS opacity:0、visibility:hidden、display:noneどれをアニメーションで使うべきか
非表示にするCSSにはopacity:0
、visibility:hidden
、display:none
の3つがある。アコーディオンやドロップダウン、モーダルのようにHTML要素を表示/非表示を切り替えたいことがあるので、それぞれの違いを確かめておく。
# まとめ
最初に動かしてわかったことをまとめておく。
非表示から表示へ、表示から非表示へアニメーションさせたい場合、opacity
とvisibility
を組み合わせるとよい。
opacity: 0
だけではコンテンツがクリックできてしまうが、visibility: hidden
と合わせることでコンテンツがクリックできなくなる。また、visibility: hidden
からvisibility: visible
にするだけでは徐々に表示される感じが表現できないが、opacity: 0
からopacity: 1
であれば徐々に表示される感じを表現できるため。
CSS Animationでdisplay
を扱いたい場合はCSSのクラスの付け替えだけでは表示から非表示へのアニメーションができない。
opacity:0 | visibility:hidden | display:none | |
---|---|---|---|
ブラウザがレンダリングする | ○ | ○ | × |
イベントを発火する | ○ | × | × |
height:0時にコンテンツをクリックできる | ○(コンテンツを非表示にしたつもりでもクリックできてしまう) | × | × |
CSS Transition | opacity:1 により徐々に透明から不透明になる。 その他のプロパティ(heightなど)もアニメーションする。 | visibility: visible により即時に表示される。 その他のプロパティはアニメーションする。 | display:block により即時に表示される。 その他のプロパティはアニメーションしない。 |
CSS Animation | 同上 | 同上 | display:block により即時に表示される。その他のプロパティはアニメーションする。しかし、display:none により即時に非表示になる。その他のプロパティはアニメーションしない。 |
# ブラウザがレンダリングするか
以下1つずつ検証していく。
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Page Title</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" type="text/css" media="screen" href="style.css">
</head>
<body>
<p>通常</p>
<div class="box"></div>
<p>opacity: 0</p>
<div class="box box--opacity">opacity</div>
<p>visibility: hidden</p>
<div class="box box--hidden">visibility</div>
<p>display: none</p>
<div class="box box--none">display</div>
<p>おわり</p>
</body>
</html>
style.css
.box {
width: 100px;
height: 100px;
background-color: #ffffc6;
}
.box--opacity {
opacity: 0;
}
.box--hidden {
visibility: hidden;
}
.box--none {
display: none;
}
opacity: 0
、visibility: hidden
ともにブラウザにレンダリングされる。ただし、コンテンツや背景色は設定されないため、何も表示されないブロックができる。一方で、display:none
は何もレンダリングされない。
ブラウザでの表示
# イベントを発火するか
何も表示されないブロックをクリックして、イベントが発火するか確かめる。
HTMLにはbody
の直前にscript
タグを追加する。
index.html抜粋
<script src="index.js"></script>
</body>
index.js
ではbox
クラスの要素を全て取得し、クリックイベントが発生したかわかるようにする。クリック時に何も表示されなければ、クリックイベントが発火しなかったことがわかる。
index.js
const boxes = document.querySelectorAll('.box');
Array.from(boxes).forEach((element)=> {
element.addEventListener('click', (event) => {
console.info(event.target);
}, false);
});
何も表示されないブロックをクリックすると、通常のブロックだけでなく、opacity:0
をクリックしたときにもイベントが発生している。一方で、visibility:hidden
はブロックとしては存在するが、クリックしてもイベントは発生しない。また、display:none
はブロックが表示されないためクリックもできない。
ブラウザでの表示
# height:0時にコンテンツをクリックできるか
ボックスのheihgt
を0にして、一見ブロックが存在しないようにしてもクリックができるか確認する。
index.html
<body>
<p>通常</p>
<div class="box box-normal">通常コンテンツ<a href="#normal">リンク</a></div>
<hr style="margin-top:30px;">
<p>opacity: 0</p>
<div class="box box-opacity">opacity:0コンテンツ<a href="#opacity">リンク</a></div>
<p>visibility: hidden</p>
<div class="box box-hidden">hiddenコンテンツ<a href="#hidden">リンク</a></div>
<p>display: none</p>
<div class="box box-none">noneコンテンツ<a href="#none">リンク</a></div>
<p>おわり</p>
<script src="index.js"></script>
</body>
style.css
p {
margin: 0;
}
.box {
width: 200px;
height: 0px;
background-color: #ffffc6;
}
.box-opacity {
opacity: 0;
}
.box-hidden {
visibility: hidden;
}
.box-none {
display: none;
}
opacity:0
の場合のみ、子要素にあるリンクは表示されていないものの、クリックできてしまう。つまり、opacity:0
とheight:0
を組み合わせて非表示にしたつもりでも、ユーザーはコンテンツをさわれてしまうので注意する🤓
ブラウザでの表示
# CSS Transitionでアニメーションするか
ボタンのクリックでコンテンツが開閉するものをつくり、CSS Transitionでのアニメーションを確かめる。
index.html
に開閉を制御するbutton
を用意する。
index.html抜粋
<body>
<button class="box-btn">通常開く</button>
<button class="box-opacity-btn">opacity: 0開く</button>
<button class="box-hidden-btn">visibility: hidden開く</button>
<button class="box-none-btn">display: none開く</button>
<p>通常</p>
<div class="box box-normal">通常コンテンツ<a href="#normal">リンク</a></div>
<hr style="margin-top:30px;">
<p>opacity: 0</p>
<div class="box box-opacity">opacity:0コンテンツ<a href="#opacity">リンク</a></div>
<p>visibility: hidden</p>
<div class="box box-hidden">hiddenコンテンツ<a href="#hidden">リンク</a></div>
<p>display: none</p>
<div class="box box-none">noneコンテンツ<a href="#none">リンク</a></div>
<p>おわり</p>
<script src="index.js"></script>
</body>
transition-property: all;
ですべてのプロパティでアニメーションするようにしておき、
ボタンが押された際にopacity
は0
から1
、visibility
はhidden
からvisible
、display
はnone
からblock
になるようにしておく。
style.css
p {
margin: 0;
}
.box {
width: 200px;
height: 0px;
background-color: #ffffc6;
transition-property: all;
transition-duration: 1s;
transition-timing-function: ease;
}
.box-normal--open {
height: 100px;
}
.box-opacity {
opacity: 0;
}
.box-opacity--open {
opacity: 1;
height: 100px;
}
.box-hidden {
visibility: hidden;
}
.box-hidden--open {
visibility: visible;
height: 100px;
}
.box-none {
display: none;
}
.box-none--open {
display: block;
height: 100px;
}
index.js
const boxBtn = document.querySelector('.box-btn');
const boxOpacityBtn = document.querySelector('.box-opacity-btn');
const boxHiddenBtn = document.querySelector('.box-hidden-btn');
const boxNoneBtn = document.querySelector('.box-none-btn');
const toggle = (targetClass) => {
const box = document.querySelector(`.${targetClass}`);
const openClass = `${targetClass}--open`;
return () => {
if (box.classList.contains(openClass)) {
box.classList.remove(openClass);
} else {
box.classList.add(openClass);
}
}
}
boxBtn.addEventListener('click', toggle('box-normal') , false);
boxOpacityBtn.addEventListener('click', toggle('box-opacity'), false);
boxHiddenBtn.addEventListener('click', toggle('box-hidden'), false);
boxNoneBtn.addEventListener('click', toggle('box-none'), false);
opacity
はいい感じに徐々に非表示から表示、表示から非表示へアニメーションする。
visibility
は非表示から表示になる際に、すぐにコンテンツが表示されてしまう。また、表示から非表示へのアニメーションでは直前までコンテンツが表示されてしまうためスムーズではない。
display
は表示、非表示だけでなく、height
についてもアニメーションされない。
ブラウザでの表示
# CSS Animationでアニメーションするか
display:none
からdisplay:block
にするときのみtransition
と挙動が違う。
index.js抜粋
const toggle = (targetClass) => {
const box = document.querySelector(`.${targetClass}`);
const openClass = `${targetClass}--open`;
const closeClass = `${targetClass}--close`;
return () => {
if (box.classList.contains(openClass)) {
box.classList.remove(openClass);
box.classList.add(closeClass);
} else {
box.classList.remove(closeClass);
box.classList.add(openClass);
}
}
}
style.css抜粋
p {
margin: 0;
}
.box {
width: 200px;
height: 0px;
background-color: #ffffc6;
animation-duration: 1s;
animation-timing-function: ease;
animation-fill-mode: forwards;
}
@keyframes none-open{
from{ display: none; height: 0; }
to { display: block; height: 100px; }
}
@keyframes none-close{
0% { display: block; height: 100px; }
99% { display: block; height: 0; }
100% { display: none; height: 0; }
}
.box-none {
display: none;
}
.box-none--open {
display: block;
height: 100px;
animation-name: none-open;
}
.box-none--close {
display: none;
height: 100px;
animation-name: none-close;
}
display
を使っても非表示から表示へのアニメーションは動くようになったが、
表示から非表示はアニメーションせず即時に非表示になってしまった。
ブラウザでの表示