【jQuery 不要】複数の details 要素のうち1つだけを開く

複数の details 要素を並べたメニューを作ったとき、details 要素がいくつも開いた状態になるのが気になってしまいました。

その問題を解決しようと「アコーディオンメニュー 1つだけ開く」あたりで検索しても、やたらと jQuery を使う方法ばかり引っかかります。なんとかそれ以外の方法で解決することにしました。

そこで、複数の details 要素のうち1つだけを開いておく方法を調べたので記事にします。

アイキャッチ
この記事の目次

方法1:素の JavaScript を使う

以下が、details 要素, summary 要素と 素の JavaScript を使ったアコーディオンメニューです。

メニュー1

メニュー1の内容がここに表示されます

メニュー2

メニュー2の内容がここに表示されます

メニュー3

メニュー3の内容がここに表示されます

メニュー1は予め開いてあります。メニュー1以外をクリックすると、メニュー1が閉じるのがわかると思います。

そしてこちらが、上のデモで使用した HTML と JavaScript です。

<details class="details-list" open="">
  <summary>メニュー1</summary>
  <p>メニュー1の内容がここに表示されます</p>
</details>

<details class="details-list">
  <summary>メニュー2</summary>
  <p>メニュー2の内容がここに表示されます</p>
</details>

<details class="details-list">
  <summary>メニュー3</summary>
  <p>メニュー3の内容がここに表示されます</p>
</details>

全ての details 要素に同じクラス名 .details-list を付けています。

// details.details-list をすべて取得
const detailsList = document.querySelectorAll('.details-list');

detailsList.forEach(details => {
  // 取得した details 要素にイベントリスナーを追加
  details.addEventListener('toggle', target => {
    // details 要素が展開された場合
    if(target.target.open){
      // 他の details 要素をチェックし、展開されているものがあれば閉じる
      details.forEach(other => {
        if(other !== target.target && other.open){
          other.open = false;
        }
      })
    }
  })
})

details 要素の開閉(open 属性の有無)は toggle イベントで検出できます。

開閉された details 要素自身は [引数].target で取得できるので、展開されたとき(self.target.open)のみ、他の detailsopen 属性が付いていたらその属性を消すようにしています。

今までイベントの発火元自体を取得するときには function 式の this を使っていましたが、アロー関数では同じ用途で this を使えないことを学びました。

方法2:details 要素の name 属性を使う

前項では JavaScript で details 要素を操作してメニューを1つだけ開くようにしましたが、なんと details 要素の name 属性を使えば、HTML だけで同じ状態を再現できます。

以下が、details 要素とその name 属性, summary 要素を使ったアコーディオンメニューです。

メニュー1

メニュー1の内容がここに表示されます

メニュー2

メニュー2の内容がここに表示されます

メニュー3

メニュー3の内容がここに表示されます

メニュー1は予め開いてあります。メニュー1以外をクリックすると、メニュー1が閉じるのがわかると思います。

そしてこちらが、上のデモで使用した HTML です。

<details name="details-list" open="">
  <summary>メニュー1</summary>
  <p>メニュー1の内容がここに表示されます</p>
</details>

<details name="details-list">
  <summary>メニュー2</summary>
  <p>メニュー2の内容がここに表示されます</p>
</details>

<details name="details-list">
  <summary>メニュー3</summary>
  <p>メニュー3の内容がここに表示されます</p>
</details>

3 つの details 要素に同じ name 属性(ここでは details-list)を付けるだけで、複数のメニューが開いた状態を回避できます。え、JavaScript 書いた意味……。

以下のサイトによると、details 要素の name 属性は2023年12月から新しく追加されたとのことです。

前項に載せた JS のコードは2023年9月頃に(大元は ChatGPTが)考えたものなので、その時点ではまさかそんな属性ができるなんて知る由もなく。あと3ヶ月待っていれば……という気持ちです笑

それにしてもすごいですよね、details 要素。数年前初めてその存在を知ったとき、HTML だけで折りたたみメニューができることに衝撃を受けたのですが、さらに進化するなんて驚きです。

編集
ホーム