JavaScriptでハンバーガーメニューを作成

スマートフォンやタブレットなど、画面の幅が狭い端末では、ナビゲーションメニューをコンパクトにまとめる工夫が必要です。そこで活躍するのが ハンバーガーメニュー です。

Contents

ハンバーガーメニューとは?

「☰」のような三本線のアイコンをタップすると、画面の端からメニューがスライドして現れます。
もう一度タップしたり、画面外をクリックするとメニューが閉じるなど、直感的に操作できて省スペースなデザインです。

 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');
});

上記のメソッドについては下記のページを参照しましょう

Web学習トレーニング
JavaScriptでDOMの取得を体験してみよう! | Webtraining - Webトレは、 初心者から実務までのWeb学習トレ... 完成イメージ 今回のテーマ JavaScriptの役割の1つは、ページ内のHTML要素を操作することです。そのためには最初に「要素をJavaScript側で取得」しなければなりません。 今...
Web学習トレーニング
JavaScriptの関数 classListで見た目を変えてみよう! | Webtraining - Webトレは、 初心者から実務まで... 完成イメージ ボタンをクリックしたら指定の場所の文字色が「class」によって変わる 今回のテーマ classList.add() / remove() / toggle() を使って、CSSクラスを切り替え...

 2.メニューをスライド表示する(CSSアニメーション)

1のメニューだとdisplay:nonedisplay:blockのみで行っているので、「パッ」と切り替わります。UX的にも「スーッ」とスライドして出てくる方が自然でスマートです。次は、CSSの transformtransition を使って、右からスライドインするように改良してみましょう。

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); /* 画面内にスライド */
}
Web学習トレーニング
動き:ハンバーガーボタンをCSSで作る | Webtraining - Webトレは、 初心者から実務までのWeb学習トレーニ... 作成ファイル・保存場所 css-practice フォルダを作成し、以下のファイルを用意してください。 ファイル名内容hamburger-menu.htmlハンバーガーメニュー用のHTMLファイルha...

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');

でメニューを閉じる、という流れです。

Web学習トレーニング
JavaScriptのif文で条件分岐を学ぼう! | Webtraining - Webトレは、 初心者から実務までのWeb学習トレー... 作成ファイル・保存場所 js-basic フォルダに以下のファイルを作成してください。 ファイル名内容js-basic03.html条件分岐を体験するJavaScript内蔵HTMLファイルjs-basic__...

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 イベントリスナーを追加します。
  • ユーザーがクリックしたとき、hamburgermenu.active クラスを外します。
Contents
吹き出し
WebTraining AI
WebTraining AI へようこそ!
HTML / CSS / JavaScript / Wordpress/ PHP など、 コードやプログラミングについて何でも質問できます。