前回、JavaScriptのイベント処理で横からにゅっと出し入れ出来るメニューバーを作成しました。
今回はその中に「アコーディオンメニュー」を実装する方法についてまとめたいと思います。
こんな感じになります。↓
前回までのhtml/css、javascriptファイルに記述を追加していきます。
なお、javaScriptはライブラリを使用していません。
HTML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>メニュー</title> <link rel="stylesheet" href="css/style.css"> </head> <body> <header><h1>Hallo!</h1> <p id="menu">menu</p> <nav id="nav" class="navi"> <div id="la1">オアシス</div> <ul id="menu1" class="close1"> <li>ドントルックバック</li> <li>ワンダーウォール</li> </ul> <div id="la2">レディオヘッド</div> <ul id="menu2" class="close2"> <li>クリープ</li> </ul> <div id="la3">虎舞竜</div> <ul id="menu3" class="close3"> <li>ロード3章</li> <li>ロード638章</li> </ul> </nav> <div id="back" class="menu-background"></div> </header> <warapper> <main>main </main> <side>side </side> </warapper> <footer>footer </footer> <script src="js/main.js"></script> </body> </html> |
navタグ内のul,liの要素を上の記述の通り書き換えます。
IDの「la1~3」は、各メニューの親項目ボタン、つまりリスト表示のイベントを発生させる「トリガー」的要素のために記述しています。
ulの「menu1~3」は、リスト表示の出し入れの為に割り振っています。
CSS
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
body { margin: 0; padding: 0; } header { background:skyblue; display: flex; } header h1 { padding-left: 20px; } header p { margin-left: auto; cursor: pointer; line-height: 46px; padding-right: 20px; z-index: 150; } .navi { background: #0bd; position: fixed; overflow: hidden; top: 0; bottom: 0; right: 0; text-align: center; width: 0; transition: 0.2s; z-index: 100; } .open-menu { width: 50%; } nav { width: 30%; margin-left: auto; margin-right: auto; padding-top: 70px; } ul { padding: 0; margin: 0; width: 100%; } li { list-style: none; padding: 10px; border: solid 1px; width: 100%; background: pink; } #la1, #la2, #la3 { width: 100%; padding: 10px; border: solid 1px; background: skyblue; cursor: pointer; } .close1, .close2, .close3 { max-height: 0; overflow: hidden; transition:0.8s; } .open2 { max-height: 100vh; } .menu-background { position: fixed; top: 0; right: 0; display: block; width: 0; height: 0; background: black; opacity: 0.8; } .open { width: 100%; height: 100%; } main { background:orange; } side { background: pink; display: block; } footer { background: green; } @media screen and (min-width: 600px) { header p { display: none; } body { max-width: 1000px; } warapper { display: flex; width: 100%; } main { width: 70%; } side { width: 30%; } } |
ul,liのcssを上の記述に書き換えます。またIDのla1~3、classのclose1~3、class open2 の記述を追加します。
menuボタンとアコーディオンメニューが重なるのを防ぐために、nav要素のtopにpadding:70px;を追加しました。
ul,liについてはアコーディオンメニューのスタイルを調整しています。
前途したとおり、IDのla1~3は、メニューのリスト表示のイベントボタンとなるので、適当に見栄えを整えているだけです。
classのclose1~3は、平常時には隠しておきたいので、「max-height:0;」としています。ここがポイントで、通常の「height:0;」ではなぜか動作に適用されません。「max-height」を使うと、ちゃんと動作に反映されます。
overflow:hidden;は、平常時(height:0時)のテキストを消すためです。
transitionは、アコーディオンの開閉時のヌルっとした動きを付けます。
class open2 は、「max-height:100vh;」、つまり、アコーディオンメニューを表示させるためのclassになります。
ここもポイントで、「vh」の単位にしておかないと、なぜか正常に開閉できないので注意です。%とかは効きません。
JavaScript
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
const menu = document.getElementById("menu"); const back = document.getElementById("back"); const nav = document.getElementById("nav"); const la1 = document.getElementById("la1"); const la2 = document.getElementById("la2"); const la3 = document.getElementById("la3"); const menu1 = document.getElementById("menu1"); const menu2 = document.getElementById("menu2"); const menu3 = document.getElementById("menu3"); menu.addEventListener("click", () => { if (nav.className === "navi") { nav.classList.add("open-menu"); back.classList.add("open"); menu.textContent = "閉じる"; }else {nav.classList.remove("open-menu"); back.classList.remove("open"); menu.textContent = "menu"; } }); back.addEventListener("click", () => { back.classList.remove("open"); nav.classList.remove("open-menu"); menu.textContent = "menu"; }); la1.addEventListener("click", () => { menu1.classList.toggle("open2"); }); la2.addEventListener("click", () => { menu2.classList.toggle("open2"); }); la3.addEventListener("click", () => { menu3.classList.toggle("open2"); }); |
定数に、「la1~3」「menu1~3」のIDを取得し、代入したものを追加しています。
後は、下部にある「la1~3」のイベント処理を追加するだけです。
ここでは、「la ID」をクリックするとイベントが発生し、menuのclassに「open2」を追加するといった処理が実行されます。
つまり、リスト表示となるmenu IDには、もともと「max-height:0;」で表示が消えているclassが付与されていますが、イベント処理によって、「max-height:100vh;」のopen2 classが追加され、表示されるといった仕組みです。
また、「classList.toggle()」の記述がありますが、この場合は、イベント処理毎に、指定したIDに付随するclassがカッコ内に記述したclass名でなければ、カッコ内のclassを追加。反対に、カッコ内のclassが付与されていれば外す、といった挙動になります。
つまり、メニューの親項目をクリックするたびに開閉する動作が実現できます。