折りたたみが最も簡単 details と summary についてメモ

HTMLの<details>と<summary>を使った折りたたみがシンプルすぎて気に入ったので今更感ありますけどちょっとだけ触れておきます。開くアニメーション効果も得られたのでメモ。追記あり。

Penguin icon

素人デジタル部活の部長は折りたたみが大好きです。折りたたみ、アコーディオン、正確に何と言うか知りませんが、10年前に初めてWordPressを触ってみたときも最初に入れたプラグインは「続きを読む」で折りたたまれた本文をペロッと表示するやつでした。

<details>タグと<summary>タグ

クリックで格納・展開するHTMLタグです。JavaScriptも特殊なcssもいらないシンプルな作りです。

これを知ったのはWordPress公式テンプレートのスマホ表示におけるメニューでした。折りたたみが好きなのにあの表示が嫌いで、どこが嫌いかというとアニメーションせずに瞬時に画面が切り替わってしまうところでした。そのせいで画面で何が起きたか分からずうろたえました。

そのスマホ表示のメニューを消すためにソースを見ていて、それでこのタグを知りました。

<details>
	<summary>
		見出し
	</summary>
		展開する中身
</details>

detailsで囲み、隠さない部分をsummaryで囲みます。簡単すぎませんか。試しにやってみたら確かに期待通りに動いたので驚きました。

summary:サンプル見出し

テキストテキストテキスト中身中身

なんと簡単でしょう。▼も自動で付くし。でもなめらかな動きが伴わず瞬時に開閉します。一瞬何が起きたかわからないですね。開く、閉じるが視覚的に分かる挙動がほしいです。昨今の優れたCSSで動きを付け足せばいいだけだな、と最初は簡単に考えていました。

動きが付けられない

散々いろんなことをやったあげくにどうしてもうまくアニメーションしないので検索してこのタグについて調べてみたら(順序逆じゃね?)なんと動きを付けられない仕様なんだとか。

detailsに対してCSSでtransitionは付けられるんです。でもdetailsがうまく動いても肝心の中身が追随しなくて、みっともないアニメーションになるんですよ。開くのに0.3秒かけることはできても、その中身は瞬時に表示されてしまいます。中身が先に表示されているのに開く動きをする姿はかなり間抜けです。

動きを付けられました

いろいろ試したけどうまく行かず、だんだん面倒になってきてやがて興味を失いました。でも久しぶりに使ってみて何となく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分の高さ」です。

素のdetailsにoverflow:hidden

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 を使えばどうだろうと。
こんな感じで。

高さのテスト
サンプルテキストサンプルテキストサンプルテキストサンプルテキストサンプルテキスト
サンプルテキストサンプルテキストサンプルテキストサンプルテキストサンプルテキストサンプルテキストサンプルテキスト
サンプルテキストサンプルテキストサンプルテキスト
サンプルテキストvサンプルテキスト
サンプルテキストサンプルテキストサンプルテキスト
サンプルテキストサンプルテキストサンプルテキスト
サンプルテキストサンプルテキスト
サンプルテキストサンプルテキストサンプルテキストサンプルテキスト
高さのテスト2
サンプルテキストサンプルテキストサンプルテキスト
サンプルテキストサンプルテキストサンプルテキスト
サンプルテキストサンプルテキスト
サンプルテキストサンプルテキストサンプルテキストサンプルテキスト
高さのテスト3
サンプルテキストサンプルテキストサンプルテキストサンプルテキストサンプルテキスト
サンプルテキストサンプルテキストサンプルテキストサンプルテキストサンプルテキストサンプルテキストサンプルテキスト
サンプルテキストサンプルテキストサンプルテキスト
サンプルテキストvサンプルテキスト
サンプルテキストサンプルテキストサンプルテキスト
サンプルテキストサンプルテキストサンプルテキスト
サンプルテキストサンプルテキスト
サンプルテキストサンプルテキストサンプルテキストサンプルテキストサンプルテキストサンプルテキストサンプルテキストサンプルテキストサンプルテキスト
サンプルテキストサンプルテキストサンプルテキストサンプルテキストサンプルテキストサンプルテキストサンプルテキスト
サンプルテキストサンプルテキストサンプルテキスト
サンプルテキストvサンプルテキスト
サンプルテキストサンプルテキストサンプルテキスト
サンプルテキストサンプルテキストサンプルテキスト
サンプルテキストサンプルテキスト
サンプルテキストサンプルテキストサンプルテキストサンプルテキストサンプルテキストサンプルテキストサンプルテキストサンプルテキストサンプルテキスト
サンプルテキストサンプルテキストサンプルテキストサンプルテキストサンプルテキストサンプルテキストサンプルテキスト
サンプルテキストサンプルテキストサンプルテキスト
サンプルテキストvサンプルテキスト
サンプルテキストサンプルテキストサンプルテキスト
サンプルテキストサンプルテキストサンプルテキスト
サンプルテキストサンプルテキスト
サンプルテキストサンプルテキストサンプルテキストサンプルテキストサンプルテキストサンプルテキストサンプルテキストサンプルテキストサンプルテキスト
サンプルテキストサンプルテキストサンプルテキストサンプルテキストサンプルテキストサンプルテキストサンプルテキスト
サンプルテキストサンプルテキストサンプルテキスト
サンプルテキストvサンプルテキスト
サンプルテキストサンプルテキストサンプルテキスト
サンプルテキストサンプルテキストサンプルテキスト
サンプルテキストサンプルテキスト
サンプルテキストサンプルテキストサンプルテキストサンプルテキスト

思い通りですね。高さを固定せずして高さを固定することにより、このようにさらに理想的な動きを手に入れました。…閉じる動作についてははもう諦めましょう。

max-heightを使う

max-height を指定することで height を使うのと同じ効果が得られました。

detalis では最小限のmax-heigtを指定し、details[open] で大きな数値のmax-heightを指定します。これだけです。

details[open] のmax-heightに大きな数値を入れるわけですが、その入れ方にもいろいろあります。数値によって挙動が変わったりするので、用途に応じて使い分けるのがいいでしょう。

max-height: 9999px

大きな数値といえば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を大きくしてしまうとこうなります。
数値の大きさに応じて transition の時間を長く書くということで対処できそうですが、なんかちょっとどうでしょうね、そういう対応って。

max-height: 90vh

vhは画面サイズに応じたパーセントで、90vhと書くと画面の90%の高さということになりますね。vhはいろいろと便利な単位です。
max-heightに大きな数値を指定しつつ現実的な最大値ということですが、悪くない結果です。

この例では畳まれている部分の行数がたくさんあるときはスクロールになるようにわざと40vhで指定してみました。厳密には、中身divの高さ40vhは calcを使ってsummary分の高さを引いた数値でないといけませんがサンプルということで適当です。

高さのテスト
サンプルテキストサンプルテキストサンプルテキストサンプルテキストサンプルテキスト
サンプルテキストサンプルテキストサンプルテキストサンプルテキストサンプルテキストサンプルテキストサンプルテキスト
サンプルテキストサンプルテキストサンプルテキスト
サンプルテキスト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に大きめの数値を指定することで、アニメ効果には高さ指定が必ず必要であるという前提を崩すことができました。

追記したのであらためてまとめておきますとこうです。おおむね満足です。

  • detalis[open]
    • max-heightで大きめの数値を指定する
    • transitionで時間を指定する
  • details
    • max-height で小さめの数値を指定する(summaryが表示できる高さ)
    • overflow は hidden
  • details div(中身コンテンツ)
    • max-height をdetails[open]に合わせる
    • (厳密にはsummary分を差し引きする)
    • overflowは必要に応じて。
  • summary
    • cursor: pointer でカーソルを変えておきたい

 

広告
カテゴリーWordPress
このエントリーをはてなブックマークに追加

コメントを残す

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください