ヘッダーの第1階層のメニューをマウスオーバーすると第2階層メニューがスライドダウンし、第2階層メニューのアイコンクリックで、第2階層メニュー自体のリンクに遷移することなく、第3階層メニューをアコーディオン開閉する(クリックしていないメニューの方は自動で閉じる)、多階層メニューを作成しました。
備忘録としてcodepenにも登録してみましたので、ご参考になれば幸いです。
使用するファイル
- HTMLファイル
- CSSファイル
- JSファイル
事前準備
- jQuery及びご利用のJSファイル、CSSファイルをHTMLファイルで読み込みます(詳細説明割愛)。
- Font Awesomeに登録し、同じくHTMLファイルで読み込みます。
Font Awesomeの登録方法は下記のとおり簡単です。
Font Awesome のページで「Start for Free」を選択後、メールアドレスを登録します。
メール認証、パスワード設定と進んでいくと、コードが発行されます。
これをheadタグ内のscriptタグ内で使用します。
縦に真っすぐ並べるパターン
第3階層まで開閉した時のイメージ
CODEPENで実装と確認
「Run Pen」をクリックしてください。
See the Pen dropdown&accordion by blue moon (@blue-moon) on CodePen.
HTMLファイル
<head>
<!-- 省略 -->
<script src="https://kit.fontawesome.com/●●●●●●●●●●.js" crossorigin="anonymous"></script> <!-- ●●●部分にはご自身のfontawesomeのキーを入力 -->
<link rel="stylesheet" href="style.css"> <!-- ご自身のパスに変更 -->
</head>
<body>
<header>
<nav>
<!-- 第1階層のグローバルメニュー -->
<ul class="main-menu-list">
<li class="main-menu">
<p>menu1<i class="fas fa-angle-down"></i></p>
<!-- 第2階層メニュー -->
<ul class="category">
<li>
<a href="#">category1</a>
<!-- 第3階層メニュー開閉のためのクリック用アイコン -->
<span><i class="fas fa-minus"></i></span>
<!-- 第3階層メニュー -->
<ul class="item-list">
<li><a href="#">item1</a></li>
<li><a href="#">item2</a></li>
<li><a href="#">item3</a></li>
</ul>
</li>
<li>
<a href="#">category2</a>
<span><i class="fas fa-minus"></i></span>
<ul class="item-list">
<li><a href="#">item1</a></li>
<li><a href="#">item2</a></li>
<li><a href="#">item3</a></li>
</ul>
</li>
</ul>
</li>
<li class="main-menu">
<p>menu2<i class="fas fa-angle-down"></i></p>
<ul class="category">
<li>
<a href="#">category1</a>
<span><i class="fas fa-minus"></i></span>
<ul class="item-list">
<li><a href="#">item1</a></li>
<li><a href="#">item2</a></li>
<li><a href="#">item3</a></li>
</ul>
</li>
<li>
<a href="#">category2</a>
<span><i class="fas fa-minus"></i></span>
<ul class="item-list">
<li><a href="#">item1</a></li>
<li><a href="#">item2</a></li>
<li><a href="#">item3</a></li>
</ul>
</li>
</ul>
</li>
<li class="main-menu">menu3</li>
<li class="main-menu">menu4</li>
</ul>
</nav>
</header>
</body>
CSSファイル
/* reset&base */
body {
font-size: 12px;
color: #666;
}
ul {
margin: 0;
padding: 0;
list-style: none;
}
li {
line-height: 2.2;
}
p {
margin: 0;
}
a {
color: inherit;
text-decoration: none;
}
nav {
margin: 20px auto;
width: 80%;
}
/* 第1階層 */
.main-menu-list {
width: 100%;
display: flex;
}
.main-menu {
margin: 0 2px;
height: min-content;
width: calc(100% / 4);
text-align: center;
background-color: #e8d3ff;
cursor: pointer;
}
.fa-angle-down {
padding-left: 5px;
cursor: pointer;
}
/* 第2階層 */
.category {
padding: 0;
}
.category > li {
background-color: #c6cfff;
font-size: 0.9em;
}
.category li span {
position: relative;
cursor: pointer;
}
.fa-minus {
padding-left: 0;
}
.category li span::after {
position: absolute;
top: calc(0.25em * 1.1);
left: calc(50% - 0.1em);
width: 0.2em;
height: 0.875em;
content: "";
background-color: #666;
transition: 0.3s ease-in-out;
border-radius: 0.1em;
}
.close::after {
transform: rotate(90deg);
}
/* 第3階層 */
.item-list > li {
background-color: #deecff;
}
補足説明
マウスオーバーのターゲットとなる第1階層メニューは{display: flex}で横並びにしていますが、子要素はmarginで少し間隔を取っていまず。理由は、マウスが第1階層を横に動いた時に、マウスが離れず次のhoverイベントが発動しないためです。
JSファイル
$(window).on('load resize', function() { //読み込み時とリサイズ時
const cateList = $('header .category');
const itemList = $('header .item-list');
const moreIcon = $('.category li span');
cateList.hide();
itemList.hide();
//第1階層メニューをマウスオーバーで第2階層メニューをスライドダウン
$('nav .main-menu').hover(function() {
$(this).children(cateList).not(':animated').slideDown(300);
},function() {
moreIcon.removeClass('close');
$(itemList).slideUp(300);
$(cateList).slideUp(300);
});
//第2階層メニューのアイコンクリックで第3階層メニューをアコーディオン開閉
$(moreIcon).click(function() {
$(this).next(itemList).not(':animated').slideToggle(300);
if ($(this).hasClass('close')) {
moreIcon.removeClass('close');
}else{
$(this).addClass('close');
}
moreIcon.not($(this)).removeClass('close');
moreIcon.not($(this)).next(itemList).slideUp(300);
});
});
補足説明
第2階層のメニューアイコンをtoggleClassを使ってクラスを追加・削除してしまうと、開閉している方のメニューを閉じるアイコンに反応しないため、条件分岐でクラスを追加・削除しています。
第2階層をスライドダウンする、$(‘nav .main-menu’).hover(function() { ~ の中に、第3階層をアコーディオン開閉処理を入れると正常に動作するように見えますが、クラスの付与が動作しないことがありますので、入れ子にしない方がいいと思います。
また、hover()メソッドをmouseover()メソッドとmouseout()メソッドの組み合わせに変えてみたり、.not(‘:animated’)をstop()メソッドに変えてみたりしましたが、どうも表示や動作がうまくいかない所があり、今のところ上述のコードが一番良い方法かなと思っています。
少しずつメニューが横にずれるパターン
第3階層まで開閉した時のイメージ
CODEPENで実装と確認
See the Pen dropdown&accordion_2 by blue moon (@blue-moon) on CodePen.
CSSファイル
/* reset&base */
body {
font-size: 12px;
color: #666;
}
ul {
margin: 0;
padding: 0;
list-style: none;
box-sizing: border-box;
}
li {
line-height: 2.2;
box-sizing: border-box;
}
p {
margin: 0;
}
a {
color: inherit;
text-decoration: none;
}
nav {
margin: 20px auto;
width: 80%;
}
/* 第1階層 */
.main-menu-list {
width: 100%;
display: flex;
}
.main-menu {
margin: 0 1px; /* 間隔をあけること */
height: min-content;
width: calc(100% / 4);
text-align: center;
background-color: #e8d3ff;
cursor: pointer;
border: solid 1px #fff;
}
.fa-angle-down {
padding-left: 5px;
cursor: pointer;
}
/* 第2階層 */
.category {
padding: 0;
width: calc(100% + 2px);
border: solid 2px #fff;
transform: translate(10px, 0);
}
.category > li {
background-color: #c6cfff;
font-size: 0.9em;
}
.category li span {
position: relative;
cursor: pointer;
}
.fa-minus {
padding-left: 0;
}
.category li span::after {
position: absolute;
top: 0.25em;
left: calc(50% - 0.1em);
width: 0.2em;
height: 0.875em;
content: "";
background-color: #666;
transition: 0.3s ease-in-out;
border-radius: 0.1em;
}
.close::after {
transform: rotate(90deg);
}
/* 第3階層 */
ul.item-list {
width: calc(100% + 4px);
border: solid 2px #fff;
transform: translate(10px, 0);
}
.item-list > li {
background-color: #deecff;
}
.item-list > li:not(:last-child) {
border-bottom: solid 0.1em #fff;
}
補足説明
HTML、JSファイルは前述のパターンと同じです。
CSSのみ少しずつ手を加えています。
最後に
ヘッダーのメニューの作成って、以外に手こずるのは私だけでしょうか。
今回はスライドダウンだけで他に装飾はしていませんが、それ以外にフィックスしてみたり、スクロールしたら下りてきたり、currentクラスをつけたり、hoverやactiveの時のスタイルを変更したり、モバイルとPCで全く別のものを作ったり…、サイトの冒頭部分だからこそ拘りたくなるものの、ハマるときりがないです。
サイト作成の一番最初に取り掛かって完成したつもりでも、その後コンテンツを作っていく過程でしっくりいかず、何度も変更したりしてスパっと決まらずいつも悩まされます。
※後日追記
この多階層メニューを3D回転させてみました。