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

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

Penguin icon

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

<details>タグと<summary>タグ

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

全体を <details> で囲み、隠さないクリック部分を <summary> で囲みます。

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

たったこれだけで、折り畳みが実現できます。試しにやってみたら確かに期待通りに動きました。

1 summary:見出し部分
テキスト
中身
テキスト
テキスト
中身

(サイトで設定しているCSSが効いてしまってますが)

なんて簡単でしょう。▼も自動で付きますし。
ただ何となく腑に落ちないので、展開する中身を div で括ったりします。

<details>
  <summary>見出し</summary>
  <div class="content">展開する中身</div>
</details>

こうすると CSS でいい感じに調整できたりしますからちょっとお勧めです。尚、HTML では <details> が展開すると<details open> になります。CSS で見栄えを調整できます。

以上で満足ならこれ以上労力を掛ける必要はありません。サクッと気軽に使いましょう。

でも気に入らないことが一つあります。

 開閉の動きがない

なめらかな動きが伴わず瞬時に開閉してしまいます。動きがないと開いたり閉じたりしたときに何が起きたかわかりません。これではクリックしたユーザーが混乱します。

私、このタグをはじめて見かけたのは WordPress のスマホサイズで出てくるメニューでしたが、瞬時に表示が切り替わり、混乱しました。あれは最悪のユーザー体験と言えます。

視覚的に開閉を表現する挙動がほしいと誰もが思います。昨今の優れた CSS で動きを付け足せばいいだけだな、と最初は簡単に考えていました。

しかしこのタグ、もともとが動きを付けられない仕様なんだとか。無理矢理動かすにはいくつかのコツみたいなのがありました。CSS にいろいろ追加しますから、このタグの持つシンプルさという強みを削ることにもなります。本末転倒にならぬよう、なるべく最小の記述で動きを付けてみましょう。

開閉に動きをつける

css で動きを付けると言えば transition です。transition で開閉の動きを付けたいなら、height を指定します。

高さを指定して transition

transition には height: auto が使えないという弱点がありますから、展開したときの高さを数値で指定しなければなりません。

せっかく何も指定しなくていい details にわざわざ高さを追加しなければならない屈辱に耐え、指定します。これが実に痛いですね。

で、auto が使えないのを我慢してこんなふうに書いたとしましょう。アニメーション効果は現れません。

<!-- 動かない例 -->

details {
height: 0;
transition: all .5s;
}

details[open] {
height: 200px;
}

単に高さを追加しただけでは何も起きません。height: 0; とか無茶苦茶書いてますが、0 でも普通に表示できています。そりゃあそうで、 overflow を設定していないからですね。

そこで details に overflow: hidden; を付け足してやると当然ながら高さ0なので全部消えます。height: 0 では駄目で、開いていない状態では、summary の高さを保持しなければなりません。

  • 閉じた状態では summary の高さを指定
  • 閉じた側に overflow: hidden
  • 開いた状態では目的の height を指定
<!-- 動く例 -->

details {
height: 1.2em;
overflow: hidden;
transition: all .6s;
}

details[open] {
height: 200px;
}
2 summary:見出し部分
テキスト
中身
テキスト
テキスト
中身

 

パーフェクトとは言えませんが、開くアニメーション効果が表現できたのでとりあえず目的を達成できたことにしておきましょう。

高さを指定することが許容できる場合は、これにて完了です。

開いた高さを可変にしたい

どうしても我慢ならないのは height を数値で指定することです。あえて決まったサイズで整えたい場合を除き、実際の中身に応じたサイズに展開してほしいですよね。

やや強引で問題もありますが、できなくもないです。

max-height を使う

height を使わず、max-height を使います。そして、開いたほう details[open] に最大の数値を入れます。

.details[open] {
  max-height:9999px;
}

max-height: 9999px

大きな値と言えば9999px、かどうかは知りませんが、これだけあればまあまあ余裕ですね。

3 summary:見出し部分
テキスト
中身
テキスト
テキスト
中身

あれ?アニメーションの効果がないですね。それは一つにはtransition の指定時間との関係です。9999px に対して0.4秒を指定してしまっているので動きが速すぎるんです。もっと時間を増やさないといけません。

時間を4秒に設定したのが下ですが、ようやくわずかに動きが感じられる程度です。

4 summary:見出し部分
テキスト
中身
テキスト
テキスト
中身

ただ指定サイズと時間のバランスだけではない、ヘンテコリンな動きのようにも見えます。

max-height: 1000px

もうちょっと現実的に最大1000px ではどうでしょう。transition を2秒にしてみます。

.details {
  max-height: 24px;
  overflow: hidden;
  transition: all 2s;
}
.details[open] {
  max-height:1000px;
}
5 summary:見出し部分
テキスト
中身
テキスト
テキスト
中身

 

これくらいでどうでしょう。

中身スクロール

さて、max-height をほどほどのサイズに指定し、その高さを超える中身が希にあるという状況を想定してみます。大抵は max-height に収まるが、はみ出ることもあるってことですね。中身部分の CSS も整えておいたほうがいいでしょう。

最初のほうで書いたように、中身を<div class=”content”>で括っています。このdivに対してちょっとした指定を与えます。

まず、div.content の 高さを details の max-height と合わせます。が、同じ数値ではいけません。

max-height – summary の height

details の max-height から summary の高さを引いたものに合わせます。これが実際の中身の高さです。

例えば max-height を400pxと想定し、summary の高さが38pxとします。この場合、中身の高さは 362px になりますね。越えた場合はスクロールにします。

.sample6 details {
  max-height: 38px;
  transition: all 1s;
  overflow: hidden;
}

.sample6 details[open] {
  max-height: 400px;
}
.sample6 .content {
  max-height: 362px;
  overflow-y: scroll;
}
6 summary:見出し部分
テキスト
中身
テキスト
テキスト
中身
中身
テキスト
テキスト
中身中身
テキスト
テキスト
中身中身
テキスト
テキスト
中身中身
テキスト
テキスト
中身中身
テキスト
テキスト
中身中身
テキスト
テキスト
中身中身
テキスト
テキスト
中身

 

中途半端

max-height に大きな値を入れて、遷移時間も大きめの設定にすることで開く動きが現れました。

でも、ここの例を何度も続けてクリックすれば判るとおり、動きの効果が消えたりします。キャッシュの関係でしょうか。

遷移時間の指定と実際の時間も何だか腑に落ちない感じだし、何やら中途半端というか、無理矢理な仕上がりではあります。

閉じる動きがない

そしてもう一つ大きな欠点があります。max-height を使用することで開くアニメーション効果が得られましたが、閉じるときには効果が一切現れません。そもそも details の仕様で、閉じるときは中身が瞬時に消えます。

コメントで教えてくださったとおり、指定を逆にして閉じるアニメーションを得ると今度は開くアニメーションが消えるようです。

height を指定するやり方では、動きそのものには効いているので中身が瞬時に消えても周りの動きからアニメーションの効果を感じることができます。max-height を指定するやり方だと周囲の動きも消えますから心理的効果もありません。

これ以上はどうにもなりません。無理してでも height を指定するか、全く別の方法を選ぶしかありません。

まとめますと・・・

あらためてまとめておきますとこうです。

detailsタグは折り畳みが最も簡単。ちょちょっとタグを書くだけで折り畳みが実現できます。

しかし動きのアニメーションが付きません。これに不満がある場合、開いた状態の height を数値で決めてしまう必要があります。決めてしまえば、まあまあいい感じのアニメーション効果が得られます。

閉じるときには効きませんが、下に追いやった要素がせり上がってくるアニメーション効果が付くので我慢できなくもないです。

高さを決められない、どうしても auto の高さに展開して欲しい場合、height ではなく max-height を指定します。開いた状態では余裕のある大きな数値を指定するのが良いでしょう。

閉じた状態では、summary の高さを指定し、overflowをhidden にします。

ただし height を使うことより、より不細工なアニメーションになります。遷移時間をどのくらいにすれば良いのか判断付きにくいし、閉じる動きが完全に失われます。

開いた状態で中身をスクロールさせる場合、中身を括る要素を作って、max-height から summary 高さを引いた値を max-height に与え、overflow を scroll または auto にします。

  • details
    • max-height でsummaryが表示できる高さを指定
    • overflow  hidden
    • transition を設定
  •  detalis[open]
    • max-heightで余裕がある大きめの数値を指定する
  • div(中身コンテンツ)
    • max-height をdetails[open] – summary に合わせる
    • overflowをauto か scroll に
  • summary
    • cursor: pointer でカーソルを変えておきたい

 

追記。

もっとちゃんとしたアニメーション効果が欲しいなら、javascript の補助が必要になります。

javascript で中身の高さを測定し、[open] の height を直接記述します。
開いた状態の高ささえ判れば max-height なんか使わなくても height を指定できます。

また、そもそも details タグに似た動きの折り畳みそのものを javascript で作ってしまうのも有効でしょう。

jQuery でそういうのを作ってみたので投稿してみました。jQuery が有利なのは、アニメーションを付けるのがめちゃ楽ちんなことです。

→ 折りたたみが最も簡単 details と summary をjQueryに置き換えるメモ

折りたたみが最も簡単 details と summary をjQueryに置き換えるメモ

尚、この投稿は説明がくどかったり細かいところが間違ってたりCSSがズレていたりしたので全面的に書き直しました。

 

“折りたたみが最も簡単 details と summary についてメモ” への2件の返信

  1. 大変参考になりました。
    ちなみにmin-height ではどうでしょうか?
    自分で試してみたのですが、閉じるときはアニメーションしたのですが、開く時がいまいちアニメーションしていないようでした。
    何かのヒントになればと

    1. min-height を試す発想はありませんでした!なるほど、閉じる時にアニメーションするのですか。これは面白いですね!ありがとうございます。

noi へ返信する コメントをキャンセル

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

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