HTMLの<details>と<summary>を使った折りたたみがシンプルすぎて気に入ったので今更感ありますけどちょっとだけ触れておきます。開くアニメーション効果も得られたのでメモ。追記あり。
素人デジタル部活の部長は折りたたみが大好きです。折りたたみ、アコーディオン、正確に何と言うか知りませんが、10年前に初めてWordPressを触ってみたときも最初に入れたプラグインは「続きを読む」で折りたたまれた本文をペロッと表示するやつでした。
目次
<details>タグと<summary>タグ
クリックで格納・展開するHTMLタグです。JavaScriptも特殊なcssもいらないシンプルな作りです。
これを知ったのはWordPress公式テンプレートのスマホ表示におけるメニューの中でした。この表示が嫌いなのでスマホ表示のメニューを消すためにソースを見ていて、それでこのタグを知りました。
<details> <summary> 見出し </summary> 展開する中身 </details>
detailsで囲み、隠さない部分をsummaryで囲みます。簡単すぎませんか。試しにやってみたら確かに期待通りに動いたので驚きました。
summary:サンプル見出し
テキストテキストテキスト中身中身
なんと簡単でしょう。▼も自動で付くし。でもなめらかな動きが伴わず瞬時に開閉します。一瞬何が起きたかわからないですね。開く、閉じるが視覚的に分かる挙動がほしいです。昨今の優れたCSSで動きを付け足せばいいだけだな、と最初は簡単に考えていました。
動きが付けられない
散々いろんなことをやったあげくにどうしてもうまくアニメーションしないので検索してこのタグについて調べてみたら(順序逆じゃね?)なんと動きを付けられない仕様なんだとか。
detailsに対してCSSでtransitionは付けられるんです。でもdetailsがうまく動いても肝心の中身が追随しなくて、みっともないアニメーションになるんですよ。開くのに1秒かけることはできても、その中身は瞬時に表示されてしまいます。中身が先に表示されているのに開く動きをする姿はかなり間抜けです。
高さを指定すると動きを付けられます
いろいろ試したけどうまく行かず、だんだん面倒になってきてやがて興味を失いました。でも久しぶりに使ってみて何となくCSS書いていたら思い通りの動きをしたのでちょっと驚きました。規格が変わったのかブラウザの実装方法が変わったのか私が賢くなったのかどれでしょう。
動きのあるdetailsのサンプル
transitionを使ってますから、開く閉じるを表現するために数値で高さを作ってやらないといけなくて、そこが不自由です。height:auto ではアニメーションしないんですよ。
高ささえ指定してやれればtransitionでアニメーション効果を得られます。
details の HTMLとCSS
htmlで中身のコンテナを作る
コツというほどのことでもありませんが、detailsの中身コンテナを作っておくといいかもしれませんです。
<details> <summary> タイトル </summary> <div> 中身 </div> </details>
divで囲んだだけです。何故必要かというとdeatilsで高さを固定しますからそれとの整合性を取るためです。あと何かと中身を分けたほうが都合良かったりするし。
css概要
transitionを動かす条件として高さを数値で決めなければなりません。detailsとdetails openに高さを付けます。すると中身がうまく収まらないから中身divにもdetails openに合わせた高さを与えて、はみ出る分はoverflow-y:scroll で逃げ切ります。そしてdetails openにoverflow:hiddenを与えます。では細かくいきます。
detailsのCSS
大外のガワであるdetailsです。開いてるときは open が付きます。逆にいうとopenであるときは開いているということです(当たり前を逆に言わなくてよろしい)
[open]に高さとアニメ設定を設定
[open]に開いたときの高さを指定して、transitionを設定します。
details[open] { height: calc( 65vh + 36px); transition: all 0.4s; }
[open] に書くtransitionが開くときのアニメーションになります。これ、私ずっと逆だと思ってました。
この例で高さの指定が変なことになっていますがこれは「中身divの高さ」+「summary分の高さ」です。
openのつかない素の details のほうには、summary分の高さをheightとして設定しています。そしてoverflow:hiddenを付け足しました。こっちにoverflowを付けるってことを今までは思いつかなかったのかもしれません。
details { height: 36px; overflow: hidden; transition: all 0.2s; }
ところでopenのつかない素のdetailsに書くtransitionの指定が閉じるときの動きになります。ずっと逆だと思ってた。
中身のdiv
これまで上手くいかないとき、どうも中身divばかりいろいろ弄りすぎていたような気がします。中身divはごく普通に装飾をする以外に特に変わったことはしません。
中身divに [open] いらない taransitionいらない いるのは高さ
ポイントはというと、こちらにもdeatils openに合わせた高さを指定していることと、overflow-y:scrollがこっちにあることでしょうか。
でもそれより何より、こっちにはアニメーションの設定も不要であるということが重要でした。今までdetalsにアニメーション効果が付けられないから中身のこっちで設定しようと藻掻いていました。
まとめて書くとこうです。高さ以外の装飾的なスタイルは除いています。たったこれだけでベストな動きを手に入れました。
/* HTML */ <details> <summary>タイトル部分</summary> <div class="details_content"> 中身 </div> </details>
/* CSS */ details[open] { height: calc( 50vh + 36px); transition: all 0.4s; } details { height: 36px; overflow: hidden; transition: all 0.2s; } .details_content { height: 50vh; overflow-y: scroll; } summary { cursor: pointer; }
シンプルなHTMLとCSSですので、記事の中やテンプレートでも自在に使えます。開閉に動きをつけられないという唯一ですが最悪の欠点を克服できましたので、突如めちゃ使えるタグと化しました。
これまではInternet ExplorerはもういいとしてEdgeがこのタグに対応していませんでした。今は対応したんですか?よくわかりませんが、未対応ブラウザの場合はどう見えるんでしょう。
尚、ここに書いたCSSとHTMLには罠があって、開くときはアニメーションしますが閉じるときは実は効かないんです。中身が瞬時に消えています。開く動作を目撃したあとですから閉じる動作を見て閉じている風に脳内補完するんですね。人間心理を巧みに突いたイリュージョニストの如く高度な技でした😀
と、一度はこの記事終わってたんですが追記あります。
追記:高さを克服
taransitionでアニメ効果を付けるとき、高さを数値で指定する必要があり、これさえ我慢すればうまくいくという話でしたが、でもやっぱり高さを指定したくないという望みも消えることがなく、悪あがきしていました。ちょっと試してみたのですが上手く動くようでしたので追記で。
heightを固定する以外に手はないから、という前提で他の方法を模索したりしたわけですが、ここでふと heightの代わりに max-height を使えばどうだろうと。
こんな感じで。
heightを使わずmax-heightで試した例
高さのテスト
この三つは同じcssです。
.postid-2199 .sumple3 details[open] { max-height:1000px; transition: all .6s; } .postid-2199 .sumple3 details { max-height:1.75em; transition: all .6s; overflow: hidden; }
高さのテスト2
この三つは同じcssです。
.postid-2199 .sumple3 details[open] {max-height:1000px;transition: all .4s;}.postid-2199 .sumple3 details {max-height:1.75em;transition: all .4s;overflow: hidden;}
高さのテスト3
この三つは同じcssです。
.postid-2199 .sumple3 details[open] { max-height:1000px; transition: all .6s; } .postid-2199 .sumple3 details { max-height:1.75em; transition: all .4s; overflow: hidden; }
サンプルテキストサンプルテキストサンプルテキストサンプルテキストサンプルテキスト
サンプルテキストサンプルテキストサンプルテキストサンプルテキストサンプルテキストサンプルテキストサンプルテキスト
サンプルテキストサンプルテキストサンプルテキスト
サンプルテキストvサンプルテキスト
サンプルテキストサンプルテキストサンプルテキスト
サンプルテキストサンプルテキストサンプルテキスト
サンプルテキストサンプルテキスト
サンプルテキストサンプルテキストサンプルテキストサンプルテキストサンプルテキストサンプルテキストサンプルテキストサンプルテキストサンプルテキスト
サンプルテキストサンプルテキストサンプルテキストサンプルテキストサンプルテキストサンプルテキストサンプルテキスト
サンプルテキストサンプルテキストサンプルテキスト
サンプルテキストvサンプルテキスト
内容に応じた高さになっています。理想的な動きを手に入れました。上のサンプルはちょっと開閉の速度が速すぎて効果を感じにくいと思いますが一応効いています。その訳はこの後。
max-heightを使う
このように、max-height を指定することで height を使うのと同じ効果が得られました。
detalis では最小限のmax-heigtを指定し(summaryの高さ)、details[open] で大きな数値のmax-heightを指定します。これだけです。
details[open] のmax-heightに大きな数値を入れるわけですが、その入れ方にもいろいろあります。数値によって挙動が変わったりするので、用途に応じて使い分けるのがいいでしょう。
max-height: 9999px
大きな数値といえば9999pxですね。max-height に9999pxを指定すると、アニメーション効果が薄れます。
高さのテスト max-height 9999px
サンプルテキストサンプルテキストサンプルテキストサンプルテキストサンプルテキストサンプルテキストサンプルテキスト
サンプルテキストサンプルテキストサンプルテキスト
サンプルテキストvサンプルテキスト
サンプルテキストサンプルテキストサンプルテキスト
サンプルテキストサンプルテキストサンプルテキスト
サンプルテキストサンプルテキスト
サンプルテキストサンプルテキストサンプルテキストサンプルテキスト
.details[open] { max-height:9999px; transition: all .4s; } .details { max-height:1.75em; transition: all 0.4s; overflow: hidden; } .details div { height:auto; max-height:9999px; overflow-y:auto; }
transition で指定した 0.4s という時間指定がmax-heightの9999pxに対して効いてしまうのか、速すぎて上手くないです。やたらめったらmax-heightを大きくしてしまうとこうなります。
max-heightで指定する数値の大きさに応じて transition の時間を長く書くということで対処できます。
そんなわけで、max-heightの数値とtransitionの秒数の関係をテストしながら決めていけばいいんじゃないでしょうか。
max-height: 90vh
vhは画面サイズに応じたパーセントで、90vhと書くと画面の90%の高さということになりますね。vhはいろいろと便利な単位です。
max-heightに大きな数値を指定しつつ現実的な最大値ということですが、悪くない結果です。
この例では畳まれている部分の行数がたくさんあるときはスクロールになるようにわざと40vhで指定してみました。厳密には、中身divの高さ40vhは calcを使ってsummary分の高さを引いた数値でないといけませんがサンプルということで適当です。
高さのテスト
サンプルテキストサンプルテキストサンプルテキストサンプルテキストサンプルテキストサンプルテキストサンプルテキスト
サンプルテキストサンプルテキストサンプルテキスト
サンプルテキストvサンプルテキスト
サンプルテキストサンプルテキストサンプルテキスト
サンプルテキストサンプルテキストサンプルテキスト
サンプルテキストサンプルテキスト
サンプルテキストサンプルテキストサンプルテキストサンプルテキスト
テキストサンプルテキスト
サンプルテキストサンプルテキストサンプルテキストサンプルテキストサンプルテキストサンプルテキストサンプルテキスト
サンプルテキストサンプルテキストサンプルテキスト
サンプルテキストvサンプルテキスト
サンプルテキストサンプルテキストサンプルテキスト
サンプルテキストサンプルテキストサンプルテキスト
サンプルテキストサンプルテキストサンプルテキスト
サンプルテキストvサンプルテキスト
サンプルテキストサンプルテキストサンプルテキスト
サンプルテキストサンプルテキストサンプルテキスト
サンプルテキストサンプルテキスト
サンプルテキストサンプルテキストサンプルテキストサンプルテキストテキストサンプルテキスト
サンプルテキストサンプルテキストサンプルテキストサンプルテキストサンプルテキストサンプルテキストサンプルテキスト
サンプルテキストサンプルテキストサンプルテキスト
サンプルテキストvサンプルテキスト
サンプルテキストサンプルテキストサンプルテキスト
サンプルテキストサンプルテキストサンプルテキスト
スクロールなんかいやだ、中身は中身でフルで表示させたいんだ。というときは、max-heightを必要十分な範囲で大きな数値を指定すればいいだけのことですね。
details[open] { max-height:40vh; transition: all .4s; } details { max-height:1.75em; transition: all 0.4s; overflow: hidden; } details div { height:auto; max-height:40vh; overflow-y:auto; }
いずれにしても、max-heightに大きめの数値を指定することで、アニメ効果には高さ(height)指定が必ず必要であるという前提を崩すことができました。
ただし、max-height のやりかたでは閉じる動作にアニメ効果は付きません。瞬時に閉じてしまいます。
heightで指定するとナンチャッテですがアニメ効果が付きます。ここは残念な点ですね。
追記したのであらためてまとめておきますとこうです。おおむね満足です。
- detalis[open]
- max-heightで大きめの数値を指定する
- height: auto;
- transitionで時間を指定する(max-heightと相関関係あり)
- details
- max-height で小さめの数値を指定する(summaryが表示できる高さ)
- overflow は hidden
- details div(中身コンテンツ)
- max-height をdetails[open]に合わせる
- (厳密にはsummary分を差し引きする)
- overflowは必要に応じて auto か scroll。
- summary
- cursor: pointer でカーソルを変えておきたい
さらに追記。
開閉のアニメーションにこだわらない、高さの指定をすることに抵抗がない、こうした場合には特に面倒なこともなく簡素簡潔で最強の折りたたみタグです。
ですがなめらかな開閉アニメーションがほしい、高さ指定の煩わしさから解放されたい、そんな場合にはjQueryで似たものを用意するのが最強です。投稿しました。
→ 折りたたみが最も簡単 details と summary をjQueryに置き換えるメモ
大変参考になりました。
ちなみにmin-height ではどうでしょうか?
自分で試してみたのですが、閉じるときはアニメーションしたのですが、開く時がいまいちアニメーションしていないようでした。
何かのヒントになればと
min-height を試す発想はありませんでした!なるほど、閉じる時にアニメーションするのですか。これは面白いですね!ありがとうございます。