はじめに
CSSアドベントカレンダー3日目はCSSに関する小ネタです。
記事一覧などのページで「もっと見る」ボタンを実装するときに、height
へのCSSアニメーションのよくあるハマりポイントと私が実務で解決したときの実例を紹介したいと思います。
作りたいもの
「もっと見る」ボタンを押して続きが読める機能
「もっと見る」ボタンを押した後に元に戻る機能は考えないものとします。
また、本記事はCSSについての解説がメインなのでJSの実装などは適当もしくは記述しない場合があるので予めご了承ください。
height
が実数値のときの「もっと見る」ボタン
まずは、height: auto;
じゃないパターンです。
閉じた状態と開いた状態のスタイルを書いて、transition
の指定をするだけです。簡単。
.content {
transition: height 0.5s ease-in-out;
height: 40px;
}
.content.open {
height: 80px;
}
See the Pen Open/Close content sample by Sho Ezawa (@ezawa800) on CodePen.
height: auto;
にしたい
「もっと見る」ボタンを押したら内容をすべて表示するようにしたいケースでは、height: auto;
を指定したいこともあります。
こんな感じです。
.content {
transition: height 0.5s ease-in-out;
height: 40px;
}
.content.open {
height: auto;
}
ただし、これには問題があり…
height: auto;
には transition
が効かない!
See the Pen Open/Close content sample with height:auto; by Sho Ezawa (@ezawa800) on CodePen.
こればかりは仕様なので仕方がない。
代案を考えます。
max-height
を使うと transition
が効くようになる
よくある解決策がこちら。
.content {
transition: max-height 0.5s ease-in-out;
max-height: 40px;
}
.content.open {
max-height: 9999px;
}
See the Pen Open/Close content sample with max-height; by Sho Ezawa (@ezawa800) on CodePen.
ただし、まだちょっと問題が…
開いた状態の max-height
の値をどうするか問題
開いたときに中身をすべて表示したい場合、文字数制限などがあればいいのですが、
- 仕様上の問題で文字数制限を設定できない
- あとから仕様が変わって文字数の上限が変わってしまいスタイルが崩れた
などが考えられます。
max-height: 9999px
のような指定の問題点
それならば絶対に中身がはみ出さない十分に大きな値を指定すれば!と思うのですが…
サンプルをみてもらうと分かるように、transition
に指定した秒数と実際にアニメーションの速さが違ってしまいます。
再掲:
See the Pen Open/Close content sample with max-height; by Sho Ezawa (@ezawa800) on CodePen.
これは、transition
はあくまで max-height
を指定の秒数で変化させるため、例えば開いた状態の高さが 300px ならば max-height
が 300px になった時点でコンテンツが開き切ってしまいます。
さらに、文字数によってアニメーションの速度がバラバラになってしまい、文字数が多い場合は速すぎる、文字数が少ない場合は遅すぎる、ということが起こる可能性があります。
アニメーションをこだわって作る場合にこれではちょっと困ります…。
解決案: オーバーレイを使う
そもそもアニメーションを付けるのは、ユーザーに「画面が変化したことを気づいてもらいやすくする」のが目的なので、必ずしも height
に対してアニメーションを付ける必要はないです。
そこで、おすすめなのはオーバーレイを使ったパターンです。
height
はアニメーションなしで変化させ、オーバーレイを徐々に透明にしていくことで、擬似的に徐々にコンテンツが現れたような表現にすることができます。
.content .overlay {
transition: opacity 1s ease-in-out;
opacity: 1;
pointer-events: none;
background: linear-gradient(rgba(255, 255, 255, 0), #fff);
position: absolute;
top: 40px; /* = .content height */
left: 0;
width: 100%;
height: 100%;
}
.content.open .overlay {
opacity: 0;
}
See the Pen Open/Close content sample with overlay by Sho Ezawa (@ezawa800) on CodePen.
上記のサンプルでは、閉じた状態ではオーバーレイは overflow: hidden;
で隠れていますが、開く前からコンテンツの上のかぶせて置くことのも良いと思います。その際は、オーバーレイの下のコンテンツが選択できるように pointer-events: none;
を付けておくと良いと思います。
まとめ
-
height: auto;
だとtransition
が効かない -
max-height
を使うパターンだと十分じゃないケースもある - 解決策としてオーバーレイを使うパターンがおすすめ
その他の解決策
アニメーションの話は、「すべてのケースはこれで解決!」ということは無いと思うので、
- 他にこんな方法で解決した
- オーバーレイを使った方式だとこんな問題点があるかも
と言ったアイディア・ご意見があればコメントでもらえると嬉しいです!