ヘッダーにホバーで開閉するメガメニューを設置した際、アドレスバーからページ内コンテンツにカーソルを移動したタイミングなど、意図しない形でメニューが開いてしまうことがあります。
その対策として、メガメニュー開閉のタイミングを即時ではなく一定時間遅らせる実装を行ってみます。
サンプルコード
まずは対応前のサンプルで、CSSでの開閉を実装してみます。
<header class="header"> <div class="logo">サイト名</div> <nav class="nav"> <ul class="nav-lv1"> <li class="nav-lv1_item"> <a href="">AAAAA</a> <ul class="nav-lv2"> <li class="nav-lv2_item"><a href="">AAAAA-1</a></li> <li class="nav-lv2_item"><a href="">AAAAA-2</a></li> <li class="nav-lv2_item"><a href="">AAAAA-3</a></li> </ul> </li> 〜 略 〜 </ul> </nav> </header>
.nav-lv1_itemにホバーした際に、.nav-lv2を表示するようにします。
コードは必要な部分のみ抜粋しているので、メガメニュー部分のコードはデモページでご確認ください。
.nav-lv2 { display: none; } .nav-lv1_item:hover .nav-lv2 { display: block; }
これでメガメニューの実装ができました。
CSSでのメガメニューのデモページ
実際に確認するとわかりますが、ブラウザの上部から下部にカーソルを移動した際、意図しないタイミングででメガメニューが開かれます。
今回のデモだとそこまで気にならないですが、開かれたメガメニュー内の高さがある場合はユーザーの操作の邪魔になる懸念があります。
その対策として、メガメニューの開閉のタイミングを遅らせるようにしてみます。
タイミングを遅らせるのはJavaScriptを使用するので、まずはCSSでホバーの設定を行なっていたのをJavaScriptで行うように変更します。
イベント対象の.nav-lv1に対してjs操作用のclassを追加します。
<header class="header"> <div class="logo">サイト名</div> <nav class="nav"> <ul class="nav-lv1"> <li class="nav-lv1_item js-megamenu-item"> <a href="">AAAAA</a> <ul class="nav-lv2"> <li class="nav-lv2_item"><a href="">AAAAA-1</a></li> <li class="nav-lv2_item"><a href="">AAAAA-2</a></li> <li class="nav-lv2_item"><a href="">AAAAA-3</a></li> </ul> </li> 〜 略 〜 </ul> </nav> </header>
CSSの:hoverで表示切り替えしていたのを、classの付け替えで切り替えるように変更します。
.nav-lv2 { display: none; } .nav-lv1_item.is-hover .nav-lv2 { display: block; }
JavaScriptでホバー時・マウスアウト時にclassを出し分けるように設定します。
const megamenuNav = document.querySelectorAll('.js-megamenu-item'); for (let i = 0; i < megamenuNav.length; i++) { megamenuNav[i].addEventListener('mouseover', megamenu_over); megamenuNav[i].addEventListener('mouseleave', megamenu_leave); } function megamenu_over(e) { this.classList.add('is-hover'); } function megamenu_leave(e) { this.classList.remove('is-hover'); }
これでJavaScriptで最初のデモと同じ挙動ができました。
JavaScriptでのメガメニューのデモページ
ホバー時・マウスアウト時に一定時間開閉を行わない対応を追加します。
const megamenuNav = document.querySelectorAll('.js-megamenu-item'); for (let i = 0; i < megamenuNav.length; i++) { megamenuNav[i].addEventListener('mouseover', megamenu_over); megamenuNav[i].addEventListener('mouseleave', megamenu_leave); } let megamenuOverTimer; // ホバー時にナビを開くタイマー let megamenuLeaveTimer; // マウスアウト時にナビを閉じるタイマー function megamenu_over(e) { let t = this; // ナビを開くタイマーをキャンセル clearTimeout(megamenuOverTimer); // ホバーしたナビが開いている場合 if(t.classList.contains('is-hover')) { // ナビを閉じるタイマーをキャンセル clearTimeout(megamenuLeaveTimer); // ホバーしたナビ以外が開いている、またはいずれも開いていない場合 } else { // 一定時間後にナビを開く megamenuOverTimer = setTimeout(function() { t.classList.add('is-hover'); }, 1000); } } function megamenu_leave(e) { let t = this; // ナビを開くタイマーをキャンセル clearTimeout(megamenuOverTimer); // マウスアウトしたナビが開いていた場合 if(t.classList.contains('is-hover')) { // 一定時間後にナビを閉じる megamenuLeaveTimer = setTimeout(function() { t.classList.remove('is-hover'); }, 1000); } }
これで開閉のタイミングを遅らせたメガメニューの実装ができました。
実装内容に関してはコメントに記載している通りです。
メガメニューの開閉を遅らせるデモページ
挙動がわかりやすいように1000ミリ秒で設定していますが、実際には300程度にするとちょうどいいと思います。
jQuery版は以下になります。
$(function() { let megamenuOverTimer; // ホバー時にナビを開くタイマー let megamenuLeaveTimer; // マウスアウト時にナビを閉じるタイマー $('.js-megamenu-item').on({ mouseenter: function(e) { let t = $(this); // ナビを開くタイマーをキャンセル clearTimeout(megamenuOverTimer); // ホバーしたナビが開いている場合 if(t.hasClass('is-hover')) { // ナビを閉じるタイマーをキャンセル clearTimeout(megamenuLeaveTimer); // ホバーしたナビ以外が開いている、またはいずれも開いていない場合 } else { // 一定時間後にナビを開く megamenuOverTimer = setTimeout(function() { t.addClass('is-hover'); }, 1000); } }, mouseleave: function(e) { let t = $(this); // ナビを開くタイマーをキャンセル clearTimeout(megamenuOverTimer); // マウスアウトしたナビが開いていた場合 if(t.hasClass('is-hover')) { // 一定時間後にナビを閉じる megamenuLeaveTimer = setTimeout(function() { t.removeClass('is-hover'); }, 1000); } } }) });
コメントが承認されるまで時間がかかります。