【HTML】折り畳みウィジェットやアコーディオンを作る details 要素と summary 要素のマークアップからスタイリング

[2023-12-12 更新]

Bootstrap などの CSS フレームワークではおなじみの Collapse (コラプス) ですが、いまでは HTML のマークアップだけでも実現できるのはご存じでしょうか。Collapse とは、タイトル部分をクリックすることで、そのコンテンツの表示・非表示を切り替える仕組みで、折り畳みウィジェットとも呼ばれます。

さらに、CSS フレームワークではおなじみの Accordion (アコーディオン) も HTML のマークアップだけで実現できるようになりました。

今回は折り畳みウィジェットやアコーディオンに必要な details 要素と summary 要素のマークアップ、JavaScript での扱い方、そして、スタイリングについて解説します。

折り畳みウィジェットとは

折り畳みウィジェットは、もともと英語で Disclosure widget と呼ばれているもので、英語圏では、expander、disclosure triangle などとも呼ばれます。折り畳みウィジェットはウェブだけでなく、デスクトップアプリや OS の機能としても良く見かけます。

macOS の折り畳みウィジェットの例

ウェブ制作の業界では、人気の CSS フレームワークである Bootstrap の影響でしょうか、Collapse (コラプス) という名前のほうが良く知られているのかもしれません。また Bootstrap の Accordion(アコーディオン) も折り畳みウィジェットの派生と言えます。他には Vuetify のように Expansion panels という呼び方もあります。

details 要素と summary 要素の使い方

折り畳みウィジェットを HTML マークアップだけで作るには、details 要素と summary 要素を使います。これは説明を読むより見た方が早いでしょう。

<details>
  <summary>詳細情報</summary>
  <p>ここに詳細情報が入ります。</p>
</details>

すでにどのメジャーブラウザーでも details 要素と summary 要素はサポートされているため、見た目は異なるかもしれませんが、下に「▶ 詳細情報」と表示されているはずです。

詳細情報

ここに詳細情報が入ります。

そして「▶ 詳細情報」の部分をクリックすると、そのコンテンツが下に現れるはずです。実際にはスタイリングが必要になりますが、たったこれだけで折り畳みウィジェットが作れます。

以降、「▶ 詳細情報」の部分を「概要部」、コンテンツ部分を「詳細部」と呼びます。

details 要素と summary 要素から折り畳みウィジェットをどのように作られるのかを知っておきましょう。まず details 要素の中で最初に見つかった summary 要素を概要部とします。次に概要部として使われた summary 要素を除外した残りのコンテンツで詳細部を構成します。

もし details 要素の中に summary 要素がなければ、ブラウザーが適当な文言で概要部を勝手に作ります。実際には Chrome、Edge、Firefox、Safari で「▶ 詳細」となります。

これまでの解説を読んで、恐らく次のようなマークアップは許されるのかと勘違いするかもしれません。次のマークアップでは、details 要素の中で、概要部になるべき summary 要素が最後にマークアップされています。

<details>
  <p>ここに詳細情報が入ります。</p>
  <summary>詳細情報</summary>
</details>

実はこれでも期待通りに折り畳みウィジェットが作られます。しかし HTML 構文エラーです。HTML 仕様では、summary 要素は、親となる details 要素の最初の子要素でなければいけないと規定されているからです。

HTML 仕様上、ブラウザーの挙動としては summary 要素のマークアップ位置に柔軟性を持たせていますが、マークアップのルールとしては厳格に決められていますので、注意してください。

最初から展開したいなら open 属性

前述の実例をご覧の通り、details 要素で作った折り畳みウィジェットは、デフォルトでは折りたたまれた状態になります。これを展開した状態にしたいなら、open 属性を使います。

<details open>
  <p>ここに詳細情報が入ります。</p>
  <summary>詳細情報</summary>
</details>

以下に実例を掲載しますが、いかがでしょうか。すでに詳細部が展開した状態になっているはずです。

ここに詳細情報が入ります。

詳細情報

open 属性は論理属性です。値を持たず、属性が存在するか存在しないかだけで機能します。open="false"open="none" とマークアップしても、折り畳みウィジェットは展開された状態になりますので注意してください。

JavaScript から開閉を制御する

open 属性を JavaScript から操作して折り畳みウィジェットの開閉状態を制御することができます。また、open 属性の状態を JavaScript から読み取ることもできます。次の例ではボタンを押すことで、折り畳みウィジェットの開閉状態を反転します。

<button id="b1">開閉</button>
<details id="w1">
  <summary>詳細情報</summary>
  <p>ここに詳細情報が入ります。</p>
</details>
document.getElementById('b1').addEventListener('click', () => {
    const el = document.getElementById('w1');
    el.open = !el.open;
}, false);
詳細情報

ここに詳細情報が入ります。

開閉のイベント

JavaScript から details 要素で作った折り畳みウィジェットの開閉のイベントをキャッチすることができます。折り畳みウィジェットが操作されると、details 要素の DOM オブジェクトで toggle イベントが発生します。

次のサンプルは、details 要素の DOM オブジェクトに toggle イベントのリスナーをセットしています。イベントを受け取ると、details 要素の開閉状態をコンソールに出力します。

<p id="s2">-</p>
<details id="w2">
  <summary>詳細情報</summary>
  <p>ここに詳細情報が入ります。</p>
</details>
document.getElementById('w2').addEventListener('toggle', (event) => {
    if(event.target.open === true) {
        document.getElementById('s2').textContent = '展開されました';
    } else {
        document.getElementById('s2').textContent = '折りたたまれました。';
    }
}, false);

詳細情報

ここに詳細情報が入ります。

スタイリング

これまでのサンプルをご覧いただいた通り、ブラウザーのデフォルトスタイルはとてもそっけないもので、この状態のまま使うことはないでしょう。ここからは折り畳みウィジェットの CSS によるスタイリングを紹介します。

ブラウザのデフォルトスタイルでは折り畳みウィジェットの概要部と詳細部の境目が分かりませんので、背景と境界に色を付けてみましょう。

<details open>
  <summary>概要部</summary>
  <p>詳細部</p>
</details>
details {
  background-color: #eeeeee;
  border: 1px solid #dddddd;
}

details>summary {
  background-color: #dddddd;
  border: 1px solid #cccccc;
}  
概要部

詳細部

details 要素のデフォルトスタイルの display プロパティは block のため、折り畳みウィジェットは横いっぱいに広がっているはずです。また、パディングが無いせいか、少し見づらいのではないでしょうか。少しパディングを調整してみましょう。

details {
  ...
  padding: 0.5em;
}

details>summary {
  ...
  padding: 0.5em;
}
概要部

詳細部

今度はあえてウィジェットを折りたたんだ状態にしていますが、期待とは少し違ってないでしょうか。折りたたんでいるにもかかわらず、details 要素のパディング領域が見えています。折りたたんだ状態なら、summary 要素だけを見せたいところです。そうなると、details 要素にパディングを入れないほうが良さそうです。マークアップも少し変えてみましょう。

<details open>
  <summary>概要部</summary>
  <div class="body">
    <p>詳細部</p>
  </div>
</details>
details {
  background-color: #eeeeee;
  border: 1px solid #dddddd;
}

details>summary {
  background-color: #dddddd;
  border: 1px solid #cccccc;
  padding: 0.5em;
}
details>div.body {
  padding: 0.5em;
}
概要部

詳細部

これで、ウィジェットを折りたたんだときには summary 要素しか見えなくなりました。しかし、どうしても気になるのは summary 要素にある三角のマークでしょう。このマークを制御しているのは、summary 要素の list-style-type プロパティです。

details 要素に open 属性が存在するか否かで summary 要素の list-style-type プロパティの値を切り替えれば、summary 要素のマークを自由に変更することができます。

details>summary {
  ...
  list-style-type: "\1F60C";
}
details[open]>summary {
  list-style-type: "\1F61D";
}
概要部

詳細部

Safari 以外のメジャーブラウザーなら、ウィジェットの概要部にスマイリーが表示されているのではないでしょうか。Safari の場合は残念ながらマークはデフォルトのままになります。ただし、macOS でも Chrome であれば期待通りにスマイリーが表示されるはずです。

name 属性でアコーディオンを作る

details 要素には name 属性をセットすることができます。name 属性の値が同じ details 要素は 1 つのグループとみなされてアコーディオンを形成します。このアコーディオンは、個々のウィジットを独立して開閉できるものではなく、1 つを開くと残りはすべて閉じるユーザーインタフェースです。英語ではこれを Exclusive accordion と呼ぶようです。

では、HTML マークアップを見てみましょう。

<div>
    <details name="sample" open>
        <summary>アコーディオン項目 #1</summary>
        <div>
            <p>1 つ目のアコーディオン項目の本文です。</p>
        </div>
    </details>
    <details name="sample">
        <summary>アコーディオン項目 #2</summary>
        <div>
            <p>2 つ目のアコーディオン項目の本文です。</p>
        </div>
    </details>
    <details name="sample">
        <summary>アコーディオン項目 #3</summary>
        <div>
            <p>3 つ目のアコーディオン項目の本文です。</p>
        </div>
    </details>
</div>
アコーディオン項目 #1

1 つ目のアコーディオン項目の本文です。

アコーディオン項目 #2

2 つ目のアコーディオン項目の本文です。

アコーディオン項目 #3

3 つ目のアコーディオン項目の本文です。

このサンプルには 3 つの details 要素がマークアップされています。すべての details 要素に name 属性がセットされていますが、いずれも値は "sample" です。これら 3 つの details 要素が 1 つのグループを形成しています。1 つの項目を開けば、他の項目は閉じ、必ず 1 つしか開いていない状態にすることができます。

WHATWG HTML 仕様では、アクセシビリティ上の観点から、同じグループの details 要素は何かしらの要素 (section 要素など) でグループ化するべきと言っています。上記サンプルでは div 要素で囲んでいます。

上記サンプルでは 3 つの details 要素のうち 1 つだけに open 属性をマークアップしています。同じ name 属性をセットした details 要素の中で open 要素をマークアップしてもよいのは 1 つに限られますので注意してください。もし複数の details 要素に同じ値の name 属性をマークアップしたとしても、開いた状態でレンダリングされるのは最初の 1 つだけです。

一見、便利そうなアコーディオンではありますが、使いどころには注意しましょう。状況によってはユーザーにとって不便になるからです。たとえば比較検討に使われる情報なら、他が自動的に閉じてしまうのは困ります。基本的には、それぞれの項目が独立したものの場合に利用するのが良いでしょう。FAQ などでは役に立ちそうです。

この name 属性を使ったアコーディオンですが、2023 年 12 月現在、Chrome、Edge、Safari がサポートしています。Firefox ではまだ利用することができません。最新の情報は caniuse.com を参照してください。

まとめ

CSS フレームワークなどが作り出すウィジェットが優れている点を挙げるとしたら、開閉時のアニメーションでしょうか。残念ながら details 要素と summary 要素によるウィジェットには開閉時のアニメーションの機能は用意されていません。

個人的な好みを言うと、アニメーションは好きではないため、CSS フレームワークを使っていても開閉時のアニメーションはオフにしていました。そう考えると、少なくとも私にとっては details 要素と summary 要素によるウィジェットやアコーディオンで何ら困ることはありません。もし CSS フレームワークを使わない、または、使えない状況においては、きっと details 要素と summary 要素は役に立つことでしょう。

今回は以上で終わりです。最後まで読んでくださりありがとうございました。それでは次回の記事までごきげんよう。

Share