前回はCSSだけでメニューを開閉しました。このレッスンでは、同じメニューをJavaScriptで動かします。見た目はほとんど変わりませんが、キーボードや読み上げソフトでも使えるようになり、実際のサイトで安心して使えるメニューになります。

CSSだけの方法との違い
| CSSだけ(前回) | JavaScript(今回) | |
|---|---|---|
| ボタン | チェックボックス+label | button 要素 |
| 開閉の合図 | チェックが入っているか(:checked) | クラス is-open が付いているか |
| 切り替える人 | ブラウザ(チェックボックスの機能) | JavaScript |
CSSだけの方法では、チェックボックスのON・OFFを合図にしていました。JavaScriptでは、ボタンを押したら、ボタンとメニューに is-open というクラスを付けたり外したりします。CSSには「is-open が付いていたら開いた見た目にする」と書いておきます。
手順1:HTML
<header class="header">
<a class="logo" href="#">Sample Cafe</a>
<button class="menu-toggle" id="menu-toggle" aria-controls="global-nav" aria-expanded="false">
<span class="bar"></span>
<span class="bar"></span>
<span class="bar"></span>
<span class="visually-hidden">メニュー</span>
</button>
<nav class="global-nav" id="global-nav">
<ul>
<li><a href="#">HOME</a></li>
<li><a href="#">コンセプト</a></li>
<li><a href="#">メニュー</a></li>
<li><a href="#">店舗案内</a></li>
<li><a href="#">お問い合わせ</a></li>
</ul>
</nav>
</header>
前回との違いは次の3つです。
- チェックボックスと
labelの代わりに、button要素を使います。buttonはもともと「押すための部品」なので、キーボードでも操作でき、読み上げソフトにも「ボタン」と伝わります。 - ボタンとメニューに
idを付けます。JavaScriptから見つけるための目印です。 aria-controlsとaria-expandedは、読み上げソフトに「このボタンはどのメニューを開くのか」「いま開いているか」を伝えるための属性です。見た目には影響しません。
三本線のボタンには文字がないので、画面には見せずに読み上げソフトにだけ伝える「メニュー」という文字を入れています(visually-hidden)。
手順2:CSS
三本線とメニューのスタイルは、前回とほとんど同じです。#menu-btn:checked ~ と書いていたところを、.is-open に置き換えます。
/* ボタンに is-open が付いたら「×」に */
.menu-toggle.is-open .bar:nth-child(1) { transform: translateY(12px) rotate(45deg); }
.menu-toggle.is-open .bar:nth-child(2) { opacity: 0; }
.menu-toggle.is-open .bar:nth-child(3) { transform: translateY(-12px) rotate(-45deg); }
.menu-toggle.is-open .bar { background: #fff; }
/* メニューに is-open が付いたら画面の中へ */
.global-nav.is-open {
right: 0;
}
button 要素には、ブラウザが最初から枠線や背景色を付けています。border: none と background: none で消しておきましょう。CSSの全体は、サンプルページのソース(右クリック →「ページのソースを表示」)で確認できます。
手順3:JavaScript
いよいよJavaScriptです。</body> の直前に書きます。
<script>
const toggle = document.getElementById('menu-toggle'); // ボタン
const nav = document.getElementById('global-nav'); // メニュー
// ボタンを押すたびに、開く・閉じるを切り替える
toggle.addEventListener('click', function () {
const isOpen = toggle.classList.toggle('is-open'); // 付いていなければ付ける・付いていれば外す
nav.classList.toggle('is-open', isOpen); // メニューもボタンと同じ状態に
toggle.setAttribute('aria-expanded', isOpen); // 開いているかどうかを読み上げソフトに伝える
});
// メニューの中のリンクを押したら閉じる(ページ内リンク用)
nav.querySelectorAll('a').forEach(function (link) {
link.addEventListener('click', function () {
toggle.classList.remove('is-open');
nav.classList.remove('is-open');
toggle.setAttribute('aria-expanded', false);
});
});
</script>
1行ずつ読んでみましょう
| コード | 意味 |
|---|---|
document.getElementById('menu-toggle') | id が menu-toggle の要素(ボタン)を見つける |
const toggle = … | 見つけたボタンに「toggle」という名前を付けて、あとで使えるようにする |
addEventListener('click', function () { … }) | ボタンが押されたら、{ } の中を実行する |
classList.toggle('is-open') | クラス is-open が付いていなければ付け、付いていれば外す。結果(付いたら true)が返ってくる |
nav.classList.toggle('is-open', isOpen) | メニューにも、ボタンと同じ状態で is-open を付け外しする |
setAttribute('aria-expanded', isOpen) | 開いているかどうか(true / false)を読み上げソフトに伝える |
【ポイント】JavaScriptがするのは「クラスの付け外し」だけ
メニューが動く見た目は、すべてCSSに書いてあります。JavaScriptは「押されたらクラスを付け外しする」だけ。見た目を変えたいときはCSSを、動くきっかけを変えたいときはJavaScriptを直す、と役割が分かれているので、あとから直しやすくなります。
リンクを押したら閉じる
後半の querySelectorAll('a') の部分は、「メニューの中のリンクを押したら、メニューを閉じる」処理です。同じページの中を移動するリンク(#concept など)を押したとき、メニューが開いたままにならないようにしています。
以前の教材(jQuery)で学んだ人へ
以前の教材では、jQueryを使って次のように書いていました。
$('.toggle').click(function () {
$(this).toggleClass('active');
$('nav').slideToggle();
});
やっていることは今回と同じで、「押されたらクラスを付け外しする」です。いまはjQueryを読み込まなくても、素のJavaScript(バニラJavaScriptとも呼びます)で同じくらい短く書けます。新しく作るサイトでは、今回の書き方をおすすめします。
まとめ
- ボタンには
button要素を使う。キーボードや読み上げソフトでも操作できる - JavaScriptは、ボタンが押されたら
classList.toggleでクラスを付け外しするだけ - 見た目の変化は、CSSで「クラスが付いていたら」と書いておく
aria-expandedで、開いているかどうかを読み上げソフトに伝える
次のレッスンでは、PCの幅ではメニューを横並びにして、ハンバーガーメニューを完成させます。