【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
)のみ、他の details
に open
属性が付いていたらその属性を消すようにしています。
今までイベントの発火元自体を取得するときには 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 だけで折りたたみメニューができることに衝撃を受けたのですが、さらに進化するなんて驚きです。