JavaScript

✅ 【JavaScript】ハンバーガーメニューを開いたときに背景をスクロールさせない方法

スマホサイトでよく見かけるハンバーガーメニュー。 メニューを開いたとき、背景(ページ全体)がスクロールできてしまうのはUX的に問題があります。

今回は、jQuery を使ってハンバーガーメニューを開いたときに背景を固定(スクロールできないように)する方法を、初心者向けにわかりやすく解説します。


🧩 なぜ背景がスクロールするのを防ぐ必要があるの?

ハンバーガーメニューを開いた状態で背景がスクロールできると、以下のような問題が発生します:

  • 誤タップの原因に: ユーザーが意図せず背景のリンクをタップしてしまう
  • 視覚的な違和感: メニューを見ているのに背景が動くと混乱する
  • 操作性の低下: どちらを操作しているのか分かりにくくなる

背景を固定することで、ユーザーはメニューの操作に集中でき、より快適なユーザー体験を提供できます。


✅ 実装方法(jQuery)

① HTML構造の例

BEM記法を使用した、セマンティックで保守性の高いHTML構造です。

<!-- ハンバーガーボタン -->
<button 
  class="header__nav-toggle" 
  type="button"
  aria-expanded="false" 
  aria-controls="header-nav" 
  aria-label="メニューを開く"
>
  <span class="header__nav-toggle-line"></span>
  <span class="header__nav-toggle-line"></span>
  <span class="header__nav-toggle-line"></span>
</button>

<!-- メニュー本体 -->
<nav class="header__nav" id="header-nav" aria-label="グローバルナビゲーション">
  <ul class="header__nav-list">
    <li class="header__nav-item">
      <a href="#" class="header__nav-link">ホーム</a>
    </li>
    <li class="header__nav-item">
      <a href="#" class="header__nav-link">サービス</a>
    </li>
    <li class="header__nav-item">
      <a href="#" class="header__nav-link">お問い合わせ</a>
    </li>
  </ul>
</nav>

ポイント:

  • aria-expanded: メニューの開閉状態をスクリーンリーダーに伝える
  • aria-controls: ボタンが制御する要素を明示
  • aria-label: ボタンの用途を明確に説明

② CSSで基本スタイル

/* 背景スクロール防止用 */
body.no-scroll {
  overflow: hidden;
}

/* ハンバーガーメニュー */
.header__nav {
  display: none;
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100vh;
  background: #fff;
  z-index: 1000;
}

.header__nav.is-active {
  display: block;
}

CSS のポイント:

  • position: fixed: メニューを画面に固定
  • z-index: 1000: メニューを最前面に表示
  • .is-active: 状態を表すクラスで開閉を制御

③ jQueryでメニュー開閉+背景固定

// ハンバーガーメニュー
$(document).ready(function() {
  const $navToggle = $('.header__nav-toggle');
  const $headerNav = $('.header__nav');
  const $navLinks = $('.header__nav-link');
  
  let scrollPosition = 0;

  // メニューボタンクリックイベント
  $navToggle.on('click', function() {
    const isActive = $(this).hasClass('is-active');
    
    if (!isActive) {
      scrollPosition = $(window).scrollTop();
      $(this).addClass('is-active');
      $headerNav.addClass('is-active');
      
      $('body').css({
        'position': 'fixed',
        'width': '100%',
        'top': -scrollPosition + 'px'
      });
      
      $(this).attr({
        'aria-label': 'メニューを閉じる',
        'aria-expanded': 'true'
      });
    } else {
      $(this).removeClass('is-active');
      $headerNav.removeClass('is-active');
      
      $('body').css({
        'position': '',
        'width': '',
        'top': ''
      });
      
      document.documentElement.style.scrollBehavior = 'auto';
      $(window).scrollTop(scrollPosition);
      
      requestAnimationFrame(function() {
        document.documentElement.style.scrollBehavior = 'smooth';
      });
      
      $(this).attr({
        'aria-label': 'メニューを開く',
        'aria-expanded': 'false'
      });
    }
  });

  // ナビゲーションリンククリックイベント
  $navLinks.on('click', function(e) {
    e.preventDefault();
    
    const href = $(this).attr("href");
    const $targetElement = $(href);
    
    $navToggle.removeClass('is-active');
    $headerNav.removeClass('is-active');
    
    $('body').css({
      'position': '',
      'width': '',
      'top': ''
    });
    
    document.documentElement.style.scrollBehavior = 'auto';
    $(window).scrollTop(scrollPosition);
    
    $navToggle.attr({
      'aria-label': 'メニューを開く',
      'aria-expanded': 'false'
    });
    
    if ($targetElement.length) {
      setTimeout(function() {
        document.documentElement.style.scrollBehavior = 'smooth';
        window.location.hash = href;
      }, 50);
    } else {
      requestAnimationFrame(function() {
        document.documentElement.style.scrollBehavior = 'smooth';
      });
    }
  });

  // ESCキーでメニューを閉じる
  $(document).on('keydown', function(e) {
    if (e.key === 'Escape' && $navToggle.hasClass('is-active')) {
      $navToggle.trigger('click');
    }
  });
});

🔍 実装のポイント解説

背景スクロール防止の仕組み

1.スクロール位置を保存

scrollPosition = $(window).scrollTop();

メニューを開く前のスクロール位置を変数に保存します。

2.body を固定

   $('body').css({
       'position': 'fixed',
       'width': '100%',
       'top': -scrollPosition + 'px'
   });
  • position: fixed: body を固定してスクロールを防止
  • width: 100%: iOS で表示崩れを防ぐ
  • top: -scrollPosition + 'px': 表示位置がずれないように調整

3.元の位置に復元

$('body').css({
       'position': '',
       'width': '',
       'top': ''
   });
   $(window).scrollTop(scrollPosition);

メニューを閉じたら、保存しておいたスクロール位置に戻します。

🎯 デモ

実際の動作を確認できるCodePenのデモを用意しました。

https://codepen.io/npupiwfh-the-sans/pen/JoKajPo

ハンバーガーメニューを開いた状態でスクロールを試すと、背景が固定されていることが確認できます。


⚠️ よくあるミスと解決策

ミス内容原因解決策
メニューを閉じるとページ上部に戻るスクロール位置の保存・復元を忘れているscrollPosition 変数でスクロール位置を保存し、scrollTop() で復元する
iOS で表示がずれるbody の幅が指定されていないwidth: 100% を必ず設定する
メニューを開いた位置がずれるtop プロパティの設定を忘れているtop: -scrollPosition + 'px' で現在位置を保持する

💡 さらなる改善案

ESC キーでメニューを閉じる

アクセシビリティを向上させるため、ESC キーでメニューを閉じられるようにします。

// ESCキーでメニューを閉じる
$(document).on('keydown', function(e) {
    if (e.key === 'Escape' && $navToggle.hasClass('is-active')) {
        $navToggle.trigger('click');
    }
});

スムーズスクロールとの連携

アンカーリンクをクリックした際、メニューを閉じてからスムーズにスクロールさせることもできます。

✅ まとめ

ハンバーガーメニューを開いたときに背景が動いてしまうのは、ユーザー体験を損なう原因になります。

今回紹介した方法を使えば、わずかなコードで快適なメニュー操作を実現できます。ぜひあなたのサイトにも取り入れてみてください!

実装のポイント:

  • ARIA 属性でアクセシビリティにも配慮
  • スクロール位置の保存と復元が重要
  • position: fixedtop の組み合わせで表示位置を維持

✉️ あなたのリクエスト教えてください!

「この記事わかりやすかった!」
「他にもこんな記事書いてほしい!」
そんな声を、ぜひXのDMで教えてください!😊📩

できるだけリクエストにお応えして、今後の記事作成に活かしていきます

▶️ Xもフォローしてもらえるとめちゃくちゃ嬉しいです!

hisa

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA