スマートフォンやタブレットなど、画面の幅が狭い端末では、ナビゲーションメニューをコンパクトにまとめる工夫が必要です。そこで活躍するのが ハンバーガーメニュー です。
ハンバーガーメニューとは?
「☰」のような三本線のアイコンをタップすると、画面の端からメニューがスライドして現れます。
もう一度タップしたり、画面外をクリックするとメニューが閉じるなど、直感的に操作できて省スペースなデザインです。
1.基本機能:クリックでメニューを開閉する
まずは、下記のようなクリックするとメニューが表示/非表示になる、一番簡単なハンバーガーメニューを実装してみましょう。
See the Pen ハンバーガーメニュー01 by Yoshiko Nakamura (@Yoshiko-Nakamura) on CodePen.
HTML
<div class="hamburger">
☰
</div>
<nav class="menu">
<ul>
<li><a href="#home">Home</a></li>
<li><a href="#about">About</a></li>
<li><a href="#contact">Contact</a></li>
</ul>
</nav>
CSS
.hamburger {
font-size: 32px;
cursor: pointer;
}
.menu {
display: none; /* 最初は非表示 */
}
.menu.active {
display: block; /* アクティブ状態で表示 */
}
JavaScript
const hamburger = document.querySelector('.hamburger');
const menu = document.querySelector('.menu');
hamburger.addEventListener('click', () => {
menu.classList.toggle('active');
});
上記のメソッドについては下記のページを参照しましょう


2.メニューをスライド表示する(CSSアニメーション)
1のメニューだとdisplay:noneとdisplay:blockのみで行っているので、「パッ」と切り替わります。UX的にも「スーッ」とスライドして出てくる方が自然でスマートです。次は、CSSの transform と transition を使って、右からスライドインするように改良してみましょう。
HTMLとJavaScriptには変更はありません。
See the Pen ハンバーガーメニュー02 by Yoshiko Nakamura (@Yoshiko-Nakamura) on CodePen.
CSS
右から左にスライドインします。translateX(100%)をtranslateX(-100%)に変更すると左から右にスライドインします。
.hamburger {
font-size: 32px;
cursor: pointer;
}
.menu {
transform: translateX(100%); /* 初期位置を右外に設定 */
transition: transform 0.5s ease; /* アニメーション効果 */
background-color: #ddd; /* 背景色 */
position: fixed; /* 画面全体に固定 */
top: 0; /* 上端 */
right: 0; /* 右端 */
width: 250px; /* メニュー幅 */
height: 100%; /* 全画面の高さ */
}
.menu.active {
transform: translateX(0); /* 画面内にスライド */
}

3.メニュー外をクリックしたら閉じる
UX(使いやすさ)の観点から、メニューが開いているときに画面の他の部分をタップしても閉じられるようにしておくと親切です。
HTML/CSSには変更はありませんのでJavaSriptのみ変更します。
See the Pen ハンバーガーメニュー03 by Yoshiko Nakamura (@Yoshiko-Nakamura) on CodePen.
JavaScript
// 必要な要素を最初に一度だけ取得
const hamburger = document.querySelector('.hamburger');
const menu = document.querySelector('.menu');
// ハンバーガーアイコンのクリックイベント
hamburger.addEventListener('click', () => {
menu.classList.toggle('active');
});
// メニュー外をクリックした場合の閉じる処理
document.addEventListener('click', (e) => {
if (!menu.contains(e.target) && !hamburger.contains(e.target)) {
menu.classList.remove('active');
}
});
この処理は「全体」に対してクリックイベントを追加し、クリックした要素が .menu や .hamburger の中に含まれていなければ、メニューを閉じます。
if (!menu.contains(e.target) && !hamburger.contains(e.target)) {
menu.classList.remove('active');
}
この部分のコードは、「メニューが開いているときに、画面のどこかをクリックしたら閉じる」処理です。
e.targetは、クリックされた要素(タグ)を指します。menu.contains(e.target)は、「クリックした要素が.menuの中に含まれているかどうか」を判定します。hamburger.contains(e.target)も同様に、ハンバーガーアイコンの中をクリックしたかどうかを確認しています。
つまりこの if 文は、「クリックされた場所が .menu の中でもなく、.hamburger の中でもない」=「外側をクリックした」ということを意味しています。その場合に
menu.classList.remove('active');
でメニューを閉じる、という流れです。

4.ハンバーガーアイコンを「×」に変える
ハンバーガーアイコン(3本線)が、メニューを開いたときに「×」に変化すると、
「閉じる動作」がより直感的に伝わります。ここでは、CSSとJavaScriptを組み合わせて、見た目を変化させましょう。今回はHTML/CSS/JavaScript全て変更します。
See the Pen ハンバーガー by Yoshiko Nakamura (@Yoshiko-Nakamura) on CodePen.
HTML
<div class="openbtn" id="hamburger">
<span></span>
<span></span>
<span></span>
</div>
<nav class="menu" id="menu">
<ul>
<li><a href="#home">Home</a></li>
<li><a href="#about">About</a></li>
<li><a href="#contact">Contact</a></li>
</ul>
</nav>
CSS
/* ボタン外側 */
.openbtn {
position: relative;
background: #000;
cursor: pointer;
width: 50px;
height: 50px;
border-radius: 5px;
}
/* ボタンの中の線(3本) */
.openbtn span {
display: inline-block;
transition: all 0.4s;
position: absolute;
left: 14px;
height: 3px;
border-radius: 2px;
background: #fff;
width: 45%;
}
.openbtn span:nth-of-type(1) {
top: 15px;
}
.openbtn span:nth-of-type(2) {
top: 23px;
}
.openbtn span:nth-of-type(3) {
top: 31px;
}
/* アクティブ状態(×マーク) */
.openbtn.active span:nth-of-type(1) {
top: 18px;
left: 18px;
transform: translateY(6px) rotate(-45deg);
width: 30%;
}
.openbtn.active span:nth-of-type(2) {
opacity: 0;
}
.openbtn.active span:nth-of-type(3) {
top: 30px;
left: 18px;
transform: translateY(-6px) rotate(45deg);
width: 30%;
}
/* メニュー(前のまま) */
.menu {
transform: translateX(100%);
transition: transform 0.5s ease;
background-color: #ddd;
position: fixed;
top: 0;
right: 0;
width: 250px;
height: 100%;
}
.menu.active {
transform: translateX(0);
}
アイコンが「×」に変わる仕組み
.openbtn に .active クラスが付くと、CSSで3本の線の位置や角度が変わり、バツ印の形になります。
transform: rotate(...) を使って線を回転させています。真ん中の線は opacity: 0 で見えなくなります。JavaScriptでは、クリックのたびに .active クラスを付けたり外したりして、アイコンとメニューの両方を制御しています。
JavaScript
const hamburger = document.getElementById('hamburger');
const menu = document.getElementById('menu');
hamburger.addEventListener('click', () => {
hamburger.classList.toggle('active'); // アイコンを×に
menu.classList.toggle('active'); // メニューを表示
});
// メニュー外クリックで閉じる
document.addEventListener('click', (e) => {
if (!menu.contains(e.target) && !hamburger.contains(e.target)) {
hamburger.classList.remove('active');
menu.classList.remove('active');
}
});
アイコンを右に移動 重なり順を考える
メニューを右に移動すると必要になるのはz-indexです。また最前面にあるメニューの中のリンク先をクリックした時に、メニューが閉じるように考えます。
See the Pen アイコンを右に移動 重なり順を考えたハンバーガーメニュー by Yoshiko Nakamura (@Yoshiko-Nakamura) on CodePen.
HTML
<div class="openbtn" id="hamburger">
<span></span>
<span></span>
<span></span>
</div>
<nav class="menu" id="menu">
<ul>
<li><a href="#home">Home</a></li>
<li><a href="#about">About</a></li>
<li><a href="#contact">Contact</a></li>
</ul>
</nav>
CSS
/* ボタン外側 */
.openbtn {
position: fixed;
top: 10px;
right: 10px;
background: #57a2c7;
cursor: pointer;
width: 50px;
height: 50px;
border-radius: 5px;
z-index: 200;
}
/* ボタンの中の線(3本) */
.openbtn span {
display: inline-block;
transition: all 0.4s;
position: absolute;
left: 14px;
height: 3px;
border-radius: 2px;
background: #fff;
width: 45%;
}
.openbtn span:nth-of-type(1) {
top: 15px;
}
.openbtn span:nth-of-type(2) {
top: 23px;
}
.openbtn span:nth-of-type(3) {
top: 31px;
}
/* アクティブ状態(×マーク) */
.openbtn.active span:nth-of-type(1) {
top: 18px;
left: 18px;
transform: translateY(6px) rotate(-45deg);
width: 30%;
}
.openbtn.active span:nth-of-type(2) {
opacity: 0;
}
.openbtn.active span:nth-of-type(3) {
top: 30px;
left: 18px;
transform: translateY(-6px) rotate(45deg);
width: 30%;
}
ul {
margin: 0;
padding: 0;
list-style-type: none;
}
/* メニュー(前のまま) */
.menu {
transform: translateX(100%);
transition: transform 0.5s ease;
background-color: #ddd;
position: fixed;
top: 0;
right: 0;
width: 100%;
height: 100%;
z-index: 100;
display: flex;
align-items: center;
justify-content: center;
}
.menu.active {
transform: translateX(0);
}
JavaScript
const hamburger = document.getElementById('hamburger');
const menu = document.getElementById('menu');
const menuLinks = document.querySelectorAll('#menu a');
hamburger.addEventListener('click', () => {
hamburger.classList.toggle('active'); // アイコンを×に
menu.classList.toggle('active'); // メニューを表示
});
for (let i = 0; i < menuLinks.length; i++) {
menuLinks[i].addEventListener('click', () => {
hamburger.classList.remove('active');
menu.classList.remove('active');
});
}
querySelectorAll の戻り値は「NodeList」
const menuLinks = document.querySelectorAll('#menu a');
このコードで menuLinks には、<nav id="menu"> の中にある <a> 要素すべてが取得されます。このとき返されるのは「NodeList(ノードリスト)」という、配列のように使えるオブジェクトです。
NodeListとは?
menuLinks[0],menuLinks[1]のようにインデックス番号でアクセスできます。.lengthで要素の数が取得できます。- ただし、完全な配列(Array)ではないため、
map()やfilter()などの配列専用メソッドは使えません(ただしforEach()は使える)。
for 文でループして1つずつ取り出す
NodeList はインデックスでアクセスできるので、通常の for 文でループできます:
for (let i = 0; i < menuLinks.length; i++) {
// menuLinks[i] がそれぞれの a 要素
menuLinks[i].addEventListener('click', () => {
hamburger.classList.remove('active');
menu.classList.remove('active');
});
}
i = 0から始まり、menuLinks.length未満の間ループします。menuLinks[i]で対象の<a>要素を1つ取り出します。- その
<a>にclickイベントリスナーを追加します。 - ユーザーがクリックしたとき、
hamburgerとmenuの.activeクラスを外します。

