JavaScriptでハンバーガーメニュー

SPのメニュバーにハンバーガーメニューを実装する覚書です。

# PC完成イメージ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2627033/11843665-1b73-446f-a853-c3ce435f707c.png)

PCは、横並びのメニューとします。
PC版の説明はここでは省きますので、設定したCSSのみ最初に載せておきます。
完成図には載せておりませんが、左端にロゴ画像がある想定です。
```CSS
#header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px 16px;
}
#header .nav ul {
  display: flex;
  gap: 3
}
#header .nav ul li {
  font-size: 14px;
}
```

# SP完成イメージ
- 初期状態、メニューを閉じた時
    - 3本線で表示します。
 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2627033/f07833f7-6dbe-4cda-b337-3e2e23ff4a1d.png)
- ハンバーガーメニュークリック時
    - 縦並びのメニューとし、メニューが増える場合は自動でスクロールを設定します。
    - 画面縦幅いっぱいにメニューを広げます。
    - アイコンをバツに変化させます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2627033/e3ce4f23-1ddc-461b-8a75-e6d9a3a28c1f.png)



# HTMLの準備

```HTML
<head>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
    <script src="js/main.js"></script>
</head>
...
<header id="header">
      <div class="hamburger">
        <span></span>
        <span></span>
        <span></span>
      </div>
      <nav class="nav">
        <ul>
          <li><a href="index.html">トップ</a></li>
          <li><a href="service.html">サービス</a></li>
          <li><a href="price.html">料金</a></li>
          <li><a href="contact.html">お問い合わせ</a></li>
        </ul>
      </nav>
</header>
```

- `<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>`
jQueryのライブラリをインターネット上から読み込んでいます。
`src`属性に指定されている`https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js は`Googleが提供しているCDNという場所から、jQueryのバージョン3.7.1のファイルを読み込むためのURLです。

- `<div class="hamburger">`
この`div`が、クリックするアイコンの「領域」になります。
中に`<span>`タグが3つ入っていますが、これがハンバーガーの「3本の線」になるものです。

- `<nav class="nav">`
 この`nav`タグが、実際に表示されるメニューリスト全体です。
 PCでは最初から横に並べ表示させ、
 SPでは最初は画面外に隠しておき、ハンバーガーアイコンがタップされたら表示させます。
 
# CSS設定
CSSで、上記のHTML要素に「見た目」と隠す・見せるの「動き」を定義します。
ポイントは、「閉じた状態」と「開いた状態」の2つの状態をCSSで定義しておくことです。

## 1.ハンバーガーアイコンの作成
```CSS
/* ハンバーガーアイコンの外側 */
#header .hamburger {
    /* ボタン全体サイズを50pxの正方形に設定 */
    width: 50px;
    height: 50px;
    /* マウスポインターを指に変更 */
    cursor: pointer;
    position: fixed;
    top: 5px;
    right: 10px;
    /* 優先度を変更 */
    z-index: 30;
  }

/* ハンバーガーアイコンの内側の線 */
#header .hamburger span {
    /* ボタン横線の長さと高さを設定 */
    width: 30px;
    height: 3px;
    background-color: #000;
    /* 絶対位置指定（親は.hamburger） */
    position: absolute;
    left: 10px;
    /* ボタンが押されてXに切り替わるときの動作設定 */
    transition: all 0.4s;
  }

/* ハンバーガーメニュー3本の線の縦の位置を設定 */
#header .hamburger span:nth-of-type(1) {
    top: 16px;
  }
#header .hamburger span:nth-of-type(2) {
    top: 25px;
  }
#header .hamburger span:nth-of-type(3) {
    top: 34px;
  }
```
- `position: fixed` と `z-index`
上記を使うことで、ロゴとハンバーガーアイコンが画面の決まった位置に常に表示され、他のコンテンツの上に重なって見えるようにしています。

## 2.ハンバーガーメニューを押した時に表示されるメニュー作成
```CSS
/* メニューの外側を設定 */
#header .nav {
    /* メニュー開いた時の幅を設定 */
    width: 80%;
    /* メニュー開いた時の高さを設定（画面いっぱいに広げる） */
    height: 100vh;
    position: fixed;
    top: 0;
    right: -80%;
    z-index: 20;
    /* スライド表示する際の速度を設定 */
    transition: all 0.6s;
  }

/* メニュー内の設定 */
#header .nav ul {
    width: 100%;
    height: 100vh;
    /* メニューを縦並びにオーバーライド */
    flex-direction: column;
    /* 上下の高さを設定 */
    padding: 60px 0;
    overflow: auto;
    /* 要素同士の余白をオーバーライド */
    gap: 16px;
    text-align: center;
  }
```
- `right: -80%;`
メニューを画面の外（右側）に隠しておくことで、`nav`は**最初は見えない状態**にします。
- `transition: all 0.6s; `
`#header .nav`内の要素に変化があったときに、スライドするようにアニメーションさせるための設定です。
- `height: 100vh`と`overflow: auto`
メニューがたくさんあり、画面内に収まりきらなかった際、メニュー内でスクロールできる設定です。

:::note info
`transition: all`は、その要素に設定されている全てのプロパティにトランジション効果を適用するという意味です。
:::

## 3.メニューが開いた状態 (`.active` クラスが追加された時)
```CSS
/* ボタンを押した際の動作を作成 */
#header .nav.active {
    right: 0;
}

/* ボタンをXにするCSS */
#header .hamburger.active span:nth-of-type(1) {
    top: 24px;
    transform: rotate(-45deg);
}
#header .hamburger.active span:nth-of-type(2) {
    opacity: 0;
}
#header .hamburger.active span:nth-of-type(3) {
    top: 24px;
    transform: rotate(45deg);
}
```
- `#header .nav.active` の `right: 0;`
`「right:-80%」`で画面の外に隠してあったメニューを右寄せに表示します。
- `.hamburger.active span:nth-of-type(n) `の部分
ハンバーガーアイコンが「X」マークに変形するアニメーションを定義しています。
`transform: rotate()`で線が回転し、`opacity: 0;`で真ん中の線が消えるように見えます。

# JS設定
JavaScriptは、ユーザーがハンバーガーアイコンをクリックしたことを検知し、CSSで準備した「開いた状態」のクラス（`active`）を要素に付けたり外したりする役割を担います。
```JavaScript
$(function () {
// ハンバーガーメニューのボタンがクリックされたときの処理
 $('.hamburger').click(function () {
    $(this).toggleClass('active');
    $('#header .nav').toggleClass('active');
});

//メニュー内のリンクがクリックされた時
  $('.nav a').click(function () {
    $('.hamburger').removeClass('active');
    $('#header .nav').removeClass('active');
  });
});
```
- `$(function() { ... });`
jQueryでHTMLの読み込みが完了してからスクリプトを実行するための記述です。
- `$('.hamburger').click...`
ここでは、ハンバーガーメニューのボタンがクリックされたとき`active`クラスを追加する処理を定義しています。
`toggleClass`メソッドを使って、`hamburger`クラスと`nav`クラスに、`active`クラスの追加・削除を行います。
クラスが存在すれば削除、しなければ追加を行います。
 ※`this`の中身は`<div class="hamburger">...`の要素の中身です。
- `$('.nav a').click...`
ここでは、メニュー内のリンクがクリックされた時の処理を定義しています。
クリックされたら`removeClass`を使って、`hamburger`クラスと`nav`クラスから、`active`クラスの削除を行います。

# 挙動と表示まとめ
上記で、ハンバーガーメニューの実装は終了です。
改めて、初期状態からの挙動と表示を確認してみましょう。

## 1.ハンバーガーメニュー表示（初期状態）
 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2627033/f07833f7-6dbe-4cda-b337-3e2e23ff4a1d.png)
 初めは、
- `.hamburger` クラスには`active`クラスが付いていません。
- `.nav`クラスには`active`クラスが付いていません。

ブラウザでは以下の`CSS`が適用されています。
- ハンバーガーアイコン: 通常の三本線の形です。
- ナビゲーションメニュー: `#header .nav`に指定されている`right: -80%;`により、画面の右端から`80%`分の幅だけ外に隠れていて、見えません。

該当CSSは下記の部分となります。
```CSS
#header .hamburger {
    width: 50px;
    height: 50px;
    cursor: pointer;
    position: fixed;
    top: 9px;
    right: 10px;
    z-index: 30;
}

#header .hamburger span {
    width: 30px;
    height: 3px;
    background-color: #000;
    position: absolute;
    left: 10px;
    transition: all 0.4s;
  }

#header .hamburger span:nth-of-type(1) {
    top: 16px;
}
#header .hamburger span:nth-of-type(2) {
    top: 25px;
}
#header .hamburger span:nth-of-type(3) {
    top: 34px;
}
```

## 2.ユーザーがハンバーガーアイコンをクリックする
- イベントの発生
ユーザーが `#header .hamburger`要素をクリックすると、
すると、`main.js`の`$('.hamburger').on('click', function() { ... });`のクリックイベントリスナーが検知され、中のコードが実行されます。

### 2-(1).`JavaScript`が`active`クラスを「追加」する
#### `$(this).toggleClass('active');`の実行
`$(this)`はクリックされた要素、つまり`.hamburger`を指します。
- 現在 
`.hamburger`には`active`クラスが付いていないため、`toggleClass()`はその`active`クラスを追加します。
- 結果
`<div class="hamburger">` が`<div class="hamburger active">`に変化します。

#### `$('#header.nav').toggleClass('active');`の実行
- `$('.nav')`はナビゲーションメニュー全体（`<nav class="nav">`）を指します。
- 現在
 `.nav`には`active`クラスが付いていないため、`toggleClass()`はその`active`クラスを追加します。
- 結果
`<nav class="nav">`が`<nav class="nav active">`に変化します。

## 3.`CSS`が`active`クラスの追加に「反応」し、プロパティを変化させる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2627033/e3ce4f23-1ddc-461b-8a75-e6d9a3a28c1f.png)

`active`クラスが追加されたことで、CSSのルールが変更され、上記の表示となります。

### 3-(1)`#header .nav`の動き（メニューが表示されるアニメーション）
- `#header .nav.active { right: 0; }`というルールが新しく適用されます。
- これにより、`元の #header .nav`に指定されていた `right: -80%; の値が right: 0;`に変化します。
    - その結果、`#header .nav ul` に定義した内容が画面に表示されます。
    - `#header .nav`には`transition: all 0.6s;`が指定されているため`right`の値が`-80%`から`0`へ`0.6`秒かけて滑らかに変化します。

該当CSSは下記となります。
```CSS
  #header .nav ul {
    width: 100%;
    height: 100vh;
    flex-direction: column;
    padding: 60px 0;
    overflow: auto;
    gap: 16px;
    text-align: center;
  }

  #header .nav.active {
    right: 0;
  }
```

### 3-(2)`#header .hamburger`の`span`の動き（三本線からXマークへ変わるアニメーション）
- `span:nth-of-type(1)`:
    元の`top: 16px;`から`top: 24px;`へ、そして`transform: rotate(-45deg);`が適用されます。
- `span:nth-of-type(2)`
    元の`opacity: 1;`（初期値）から `opacity: 0;`へ
- `span:nth-of-type(3)`:
    元の`top: 34px;`から`top: 24px;`へ、そして`transform: rotate(45deg);`が適用されます。

ハンバーガーアイコンが三本線からXマークの形に、0.4秒かけて滑らかに変形します。



# 全体的な流れまとめ
- 初期状態:
    - メニューはCSSによって画面外に隠れている（`right: -80%`）。
    - ハンバーガーアイコンは三本線。
    - JavaScriptはクリックを待っている状態。
- ユーザーがクリック: 
    - ハンバーガーアイコンをタップ。
- JavaScriptの仕事:
    - クリックを検知し、`.hamburger`要素に`active`クラスを追加。
    - ナビゲーションメニューの`.nav`に`active`クラスを追加。
- CSSの仕事:
    - `active`クラスが追加されたことで、CSSの「アクティブ状態」のルール（`right: 0;`や`transform: rotate();`など）が適用される。
    - `transition`プロパティが指定されているため、これらのプロパティ値の変化が、指定された時間（0.6s や 0.4s）をかけて滑らかにアニメーションする。
- 最終状態:
    - メニューが画面内にスライドして表示される。
    - ハンバーガーアイコンがXマークに変形する。

この一連の動きが、ハンバーガーメニューの「表示」の仕組みです。