html・css js・jquery

ハンバーガーメニュー決定版

ハンバーガーメニューっていろんな作り方があるけど、わたしはこうやっているよ、という備忘録。

ポイントとしては

  • iOS 15 特有のモーダルバグ対策
  • ハンバーガーメニュー開いたときに背景が動かない
  • 背景固定はスマホのみ

あたりかなと。

特にiOS 15で発生するスクロールや高さに関連するバグを解消する目的で使用しているので、そのうちアップデートするかもです。

ちなみに、今回の背景固定はoverflow: hidden;を使用しているんですが、スマホだと効かない場合があるので、どーーーしても背景固定絶対したい!という場合は下記記事参考にどうぞ。

まずはコードから

class Ios15ModalHack {
    constructor() {
        this.init();
        this.load();
        this.resize();
    }
    init() {
        const styleTag = document.createElement('style');
        styleTag.innerText = '@media (max-width: 768px) {html.is-locked,html.is-locked body {height: calc(var(--window-inner-height) + ${window.scrollY}px - 1px);overflow: hidden;box-sizing: border-box;}}';
        document.getElementsByTagName('head')[0].insertAdjacentElement('beforeend', styleTag);
    }
    syncHeight() {
        document.documentElement.style.setProperty('--window-inner-height', `${window.innerHeight}px`);
    }
    load() {
        window.addEventListener('load', () => {
            this.syncHeight();
        });
    }
    resize() {
    // if (navigator.userAgent.match(/iPhone|Android.+Mobile/)) {
    //   return;
    // } else {
    window.addEventListener('resize', () => {
        this.syncHeight();
    });
    // }
    }
}
const App = new Ios15ModalHack();

$(".l-header__openbtn").click(function () {
    $(this).toggleClass('is-active');
    $(".l-header__navwrap").toggleClass('is-panelactive');
    $("html").toggleClass('is-locked');
});
$(".l-header__nav a").click(function () {
    if ($(this).attr('href')) {
        $(".l-header__openbtn").removeClass('is-active');
        $(".l-header__navwrap").removeClass('is-panelactive');
        $("html").removeClass('is-locked');
    }
});
<header class="l-header">
  <div class="l-header__inner">
    <!-- メニュー中身 -->
    <div class="l-header__openbtn">
      <span></span>
      <span></span>
    </div>
  </div>
</header>
.l-header {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    z-index: 10000001;
    width: 100vw;
    background-color: #fff;

    &__navwrap {
        @media (max-width: 768px) {
            background-color: #fff;
            position: fixed;
            z-index: 100001;
            right: -120%;
            top: 0;
            padding: 100px 20px;
            transition: all .6s;
            width: 100vw;
            height: 100vh;
            overflow-y: auto;
        }

        &.is-panelactive {
            @media (max-width: 768px) {
                right: 0;
            }
        }
    }
    &__openbtn {
        position: relative;
        width: 24px;
        height: 6px;
        z-index: 1000001;
        @media (min-width: 769px) {display:none;}

        span {
            position: absolute;
            z-index: 1;
            width: 100%;
            height: 2px;
            background-color: #000;
            transition: all .6s;
            &:first-of-type {
                top: 0;
            }
            &:last-of-type {
                bottom: 0;
            }
        }

        &.is-active {
            span {
                &:first-of-type {
                    transform: rotate(-45deg);
                }
                &:last-of-type {
                    bottom: auto;
                    top: 0;
                    transform: rotate(45deg);
                }
            }
        }
    }
}

htmlとcssは大事なところしか書いていないので、中身は適宜自分で追加してください。

もしリクエストあればコピペで使える、中身もありVerのやつ作ります。

コード解説

js

このコードは、iOS 15のモーダルに関する問題に対応するためのJavaScriptクラスと、ハンバーガーメニュー開閉に関するクリックイベントハンドラーを実装しています。以下に各部分の解説をします。

1. Ios15ModalHack クラス

iOS 15において、モーダルやハンバーガーメニューを開いた際のスクロールの位置や高さのバグがあったため、このクラスではその問題を修正しています。

コンストラクタ (constructor)

クラスが初期化される際に、initloadresize メソッドが呼び出され、スタイルやイベントリスナーの設定が行われます。

init() メソッド

このメソッドでは、スタイルタグを作成し、HTMLドキュメントの<head>要素に追加しています。

スタイルの内容は、スクロールが固定されている状態(html.is-locked)で、ウィンドウの高さが正確に設定されるようにしています。calc(var(--window-inner-height) + ${window.scrollY}px - 1px) という計算式で、ウィンドウの内部の高さにスクロール位置を加えた高さを設定し、1pxの誤差を調整しています。

ただ、このis-lockedクラスですが、overflow: hidden;を使用しているので特にスマホで効かない場合があります。どうしても背景固定したい場合のコードも別途まとめようと思います。→下記記事にまとめました。

syncHeight() メソッド

このメソッドは、--window-inner-height というCSSカスタムプロパティに、ウィンドウの高さを動的に設定します。これにより、ウィンドウのサイズに応じたスタイル調整が可能になります。

load() メソッド

loadイベント(ページ全体の読み込みが完了したとき)に、syncHeight() を実行するリスナーを追加しています。これにより、ページロード時に正しい高さが設定されます。

resize() メソッド

ウィンドウサイズが変更されたとき(resizeイベント)のために、syncHeight() を再実行するリスナーを追加しています。これにより、ウィンドウサイズの変更に応じて高さが更新されます。

コメントアウトされている部分では、iPhoneやAndroidのモバイルブラウザでは処理を行わないようにする可能性が示唆されていますが、ここでは無効化されています。もし特定のブラウザで不具合や問題がある場合はここで調整します。

2. jQuery クリックイベントハンドラー

$(".l-header__openbtn")$(".l-header__nav a") に対するクリックイベントを扱っています。

$(".l-header__openbtn").click(function () { ... })

この部分では、ハンバーガーメニューの開閉を実現しています。

ボタンがクリックされると、is-active クラスがトグルされ、ナビゲーションメニュー($(".l-header__navwrap"))に is-panelactive クラスをトグルします。また、html要素には is-locked クラスがトグルされ、スクロールを固定します。

$(".l-header__nav a").click(function () { ... })

ナビゲーション内のリンクがクリックされた場合、ハンバーガーメニューが自動的に閉じるようにしています。これは、クラスを取り除くことで実現され、メニューが閉じる際にスクロール固定も解除されます。

全体の動作の流れ

  1. クラス Ios15ModalHack がページロード時に初期化され、iOSで発生する高さに関連する問題を修正する。
  2. ハンバーガーメニューの開閉やスクロールの固定を、jQueryで管理。
  3. リンクがクリックされると、メニューを閉じ、スクロール固定を解除。

このコードは、iOSデバイスでのスクロールや高さの問題を解決し、モーダルウィンドウの正しい動作をサポートしながら、ハンバーガーメニューの開閉を管理しています。

html

今回は最低限のhtmlしか書いていませんが、l-header__openbtnがハンバーガーの部分になります。

より詳細なコードのリクエストがあれば追記します。

scss

cssで書いても問題ないです。今回は右からスライドしてくるハンバーガーメニューになっています。

ここもより詳細なコードのリクエストがあれば追記します。

まとめ

今回はいつも使用しているハンバーガーメニューの備忘録でした。

初心者向けというよりはより日常業務で使用するようなコードになっているかと思いますので、ぜひ使ってみてください。

もしわからないことあれば質問ください!

記事にしてほしい・解説してほしい内容のリクエストもお待ちしています。

プログラミングに興味がある方へ

プログラミングに興味があるけど踏み出すのに勇気がいる…
始めてみたけど進め方がわからない、誰かに質問したい…
ネットの教材は高くて手が出ない…
案件を取ってみたけど納品までたどり着けない…

家庭教師の経験を活かし、そんな方のお手伝いをしたいと思っています。

30万もする悪質で高額なコンテンツマーケティングに騙される前に、相談してみませんか?
相談はもちろん無料です。

こちらから、ぜひお気軽にお問い合わせください。
お仕事のご相談や記事にしてほしい内容のリクエストもお待ちしています!

-html・css, js・jquery