ドロップダウンメニューなどの作成に便利なclosestについて
closest
メソッドの使用法についてメモします。closest
メソッドは、指定したセレクタと合致する親要素を探索します。(ルートまで遡ります)
例としてドロップダウンメニューを作成して確認します。対象以外をクリックしたら閉じる動作に使用します。
サンプルコード
- HTML
<nav> <div> <h2>menu1</h2> <ul> <li>menu1-1</li> <li>menu1-2</li> <li>menu1-3</li> </ul> </div> <div> <h2>menu2</h2> <ul> <li>menu2-1</li> <li>menu2-2</li> <li>menu2-3</li> </ul> </div> <div> <h2>menu3</h2> <ul> <li>menu3-1</li> <li>menu3-2</li> <li>menu3-3</li> </ul> </div> </nav>
- CSS
nav { display: flex; } nav h2 { padding: 0 1em; cursor: pointer; } nav ul { display: none; } nav div.open ul { display: block; }
- JavaScript
(function () { const menus = document.querySelectorAll('nav div'); const menuTitles = document.querySelectorAll('nav h2'); function closeSubMenu() { menus.forEach(value => value.classList.remove('open')); } function toggleSubMenu() { const target = this.parentNode; if (target.classList.contains('open')) { target.classList.remove('open'); } else { closeSubMenu(); target.classList.add('open'); } } menuTitles.forEach(value => { value.addEventListener('click', toggleSubMenu); }); document.addEventListener('click', e => { !e.target.closest('.open') && closeSubMenu(); }); })();
今回は以下のような動作を実装します。所謂トグルメニューのような仕組みで、ドロップダウンメニューではよくある動作かと思います。
- メニューをクリックするとサブメニューが表示・非表示される
- 他の要素をクリックするとサブメニューが非表示される
メニューをクリックするとサブメニューが表示・非表示される
サンプルコードから一部抜粋します。
function closeSubMenu() { menus.forEach(value => value.classList.remove('open')); } function toggleSubMenu() { const target = this.parentNode; if (target.classList.contains('open')) { target.classList.remove('open'); } else { closeSubMenu(); target.classList.add('open'); } } menuTitles.forEach(value => { value.addEventListener('click', toggleSubMenu); });
closeSubMenu()
メニューのclass属性からopenを削除します。
toggleSubMenu()
メニュータイトルをクリックした際に動作する関数です。
class属性にopenがある場合は、openを削除します。
openがない場合はcloseSubMenu()
を実行した後に、openを追加します。
メニュータイトルの親(各メニューのdiv)を操作したいのでconst target = this.parentNode;
と指定しています。
- クリックイベントの登録
以下の記述で登録しています。メニューをクリックするとtoggleSubMenu()
を実行します。
menuTitles.forEach(value => { value.addEventListener('click', toggleSubMenu); });
他の要素をクリックするとサブメニューが非表示される
closest
を利用して実装します。
document.addEventListener('click', e => { !e.target.closest('.open') && closeSubMenu(); });
closest
は指定したセレクタと合致する親要素を探索します。ない場合はnullを返します。
上記の!e.target.closest('.open')
は、クリックした要素の親にopenがなかったら、を意味しています。その後の&& closeSubMenu()
は、条件と一致したらcloseSubMenu()
を実行します。
つまり、クリックした要素の親にopenがなかったら、サブメニューを閉じる
動作をします。
これによりメニュー以外をクリックしてもサブメニューが閉じるよう動作するようになります。
今回は親要素にopenがあるかを見ればわかりますが、closestはルートまでさかのぼって探索するので、ネストの深い要素の処理でも動作します。
PS
closest
を利用するとそれ以外をクリックした場合
を簡単に検出できて便利です。IEの対応が不要であれば利用可能です。
フローティング系のメニューも同様に対応できます。ホバーが外れたら閉じるだと都合が悪いようなケースで便利です。
ちなみにですが、開閉するメニューのみであればHTMLのdetails
タグでも作成できますが、アニメーションをつけたりする場合に不便なことが多いので、まだ現在(2022.12)はあまり利用するケースが少ない状況です。