ドロワーメニューを開いている間、ページのスクロールを禁止する方法

ドロワーメニュー
無料ツール
ドロワーメニューを自由に作成

表示位置・メニュー項目・サイズ・色・余白・閉じ方などを設定して、HTML・CSS・JavaScriptを自動生成できます。

ドロワーメニュー作成ツールを使う
新しいタブで開きます

ドロワーメニューを開いている間、背景ページのスクロールを停止する方法を紹介します。通常のドロワーメニューにスクロール制御を加え、メニューの操作中に背後のページが上下へ動かないようにします。

「メニューを開く」ボタンをクリックすると、画面左側からドロワーメニューがスライドして表示され、同時に背景ページのスクロールが停止します。マウスホイールやスマートフォンのスワイプで背景を動かそうとしても、メニューを開いた位置が維持されます。

メニューは右上の「×」ボタン、背景のクリック、Escキーで閉じられます。閉じるとスクロール制限が解除され、メニューを開く前の位置からページの閲覧を再開できます。ページの途中までスクロールした状態で開閉しても、先頭へ戻されない仕組みです。

ドロワーメニュー内のリンクは通常どおり操作でき、メニューの内容が画面の高さを超える場合は、メニュー内部だけをスクロールできます。下のデモでは、ページをスクロールしてからメニューを開き、背景の固定と閉じた後の位置の維持を確認できます。

デモ

メニューを開くと、背景ページのスクロールが停止します。

ページを上下にスクロールしてから「メニューを開く」をクリックしてください。メニュー表示中は背景ページをスクロールできず、「×」ボタン・背景のクリック・Escキーで閉じると、開く前のスクロール位置を維持したまま操作を再開できます。

コードをコピーして使おう!

<!DOCTYPE html>
<html lang="ja">
<head>
  <!-- 文字コードを設定 -->
  <meta charset="UTF-8">
  <!-- スマートフォンの画面幅に対応 -->
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <!-- ページタイトル -->
  <title>スクロールを停止するドロワーメニュー</title>
  <!-- スタイルシートを読み込む -->
  <link rel="stylesheet" href="style.css">
</head>
<body>

  <!--
    以下のmain要素はスクロール確認用のサンプル本文です。
    実際の設置時は文章を削除・置き換えてください。
    メニューを開くボタンは必要な位置に残してください。
  -->
  <main class="scroll-lock-drawer_page">

    <div class="scroll-lock-drawer_page-title">
      ページのコンテンツ
    </div>

    <p class="scroll-lock-drawer_page-text">
      このページでは、ドロワーメニューを開いている間のスクロール停止を確認できます。
      まずはページを下へスクロールしてください。
    </p>

    <p class="scroll-lock-drawer_page-text">
      通常のページと同じように、文章を読みながら下へ移動できます。
      この段階ではスクロール操作に制限はありません。
    </p>

    <p class="scroll-lock-drawer_page-text">
      ページの途中に、ドロワーメニューを開くボタンを配置しています。
      ボタンが見える位置までスクロールを続けてください。
    </p>

    <p class="scroll-lock-drawer_page-text">
      メニューを開くと、その時点のスクロール位置が維持されます。
      閉じたときに同じ位置から操作を再開できるか確認してください。
    </p>

    <!-- メニューを開くボタン -->
    <button class="scroll-lock-drawer_trigger" type="button" aria-controls="scroll-lock-drawer-panel" aria-expanded="false">
      メニューを開く
    </button>

    <p class="scroll-lock-drawer_page-text">
      ここからはボタンより下のコンテンツです。
      メニューを閉じたあとも、この続きからスクロールできます。
    </p>

    <p class="scroll-lock-drawer_page-text">
      メニューを開いている間は、背景ページを上下に移動できません。
      メニュー内の内容が長い場合は、メニュー内部だけをスクロールできます。
    </p>

    <p class="scroll-lock-drawer_page-text">
      背景の暗い部分をクリックするか、メニュー内の閉じるボタンを押すと、
      元のページへ戻れます。
    </p>

    <p class="scroll-lock-drawer_page-text">
      メニューを開く前後で、背景ページの表示位置が変化しないことを確認してください。
    </p>

  </main>

  <!-- メニュー表示中の背景 -->
  <div class="scroll-lock-drawer_overlay" aria-hidden="true"></div>

  <!-- 左側から表示するドロワーメニュー -->
  <div class="scroll-lock-drawer_panel" id="scroll-lock-drawer-panel" aria-label="ドロワーメニュー" aria-hidden="true" inert>

    <!-- メニュータイトルと閉じるボタン -->
    <div class="scroll-lock-drawer_header">
      <div class="scroll-lock-drawer_heading">メニュー</div>
      <button class="scroll-lock-drawer_close" type="button" aria-label="メニューを閉じる">
        ×
      </button>
    </div>

    <!-- メニューリンク。hrefは実際の移動先に変更してください -->
    <nav class="scroll-lock-drawer_nav" aria-label="ドロワーメニュー内のリンク">
      <a class="scroll-lock-drawer_link" href="#">ホーム</a>
      <a class="scroll-lock-drawer_link" href="#">サービス</a>
      <a class="scroll-lock-drawer_link" href="#">お知らせ</a>
      <a class="scroll-lock-drawer_link" href="#">お問い合わせ</a>
    </nav>

  </div>

  <!-- メニュー開閉とスクロール制御のJavaScriptを読み込む -->
  <script src="script.js"></script>
</body>
</html>
コードについて 本記事のコードはサンプルコードです。ご利用前に必ず動作確認を行ってください。 免責事項 本コードの利用により発生した損害について、当サイトは一切の責任を負いません。

コードをコピーする場合 使用するコードのタブを選び、「このタブをコピー」をクリックすると、現在表示中のコードをコピーできます。 サンプル一式を取得する場合 HTML・CSSなどすべてのファイルが必要な場合は、「サンプル一式をダウンロード」をクリックしてください。
Q ダウンロードしたけど、これをネット上で動かすにはどうするの?
A

Googleアカウントがあれば、InfinityFreeで無料サーバーを作成して、ダウンロードしたサンプルをネット上で動かせます。

InfinityFreeを初めて利用する場合は①から、すでにサーバーを作成している場合は②へ進んでください。

ファイル構成と設置方法

今回のサンプルは、HTML・CSS・JavaScriptの3つのファイルで構成されています。HTMLではドロワーメニューと開閉ボタンを配置し、CSSではメニューの外観・スライド表示・背景のスクロール停止を設定しています。JavaScriptではメニューの開閉に合わせてスクロールを禁止・再開し、開く前のスクロール位置を維持します。

sample/
  ├── index.html
  ├── style.css
  └── script.js

各ファイルの役割
index.htmlには、メニューを開くボタン、背景を覆うオーバーレイ、ドロワーメニュー本体、閉じるボタン、メニューリンクを記述しています。style.cssでは、メニューの横幅・背景色・余白・スライド動作と、背景ページのスクロール停止に使用するスタイルを設定しています。script.jsでは、開閉操作・スクロール制御・開閉状態の更新を行っています。

設置方法・使い方
「サンプル一式をダウンロード」をクリックしてZIPファイルを保存し、展開してください。sampleフォルダ内のindex.html・style.css・script.jsを同じ階層に配置し、index.htmlをブラウザで開きます。
ページを下へスクロールして「メニューを開く」をクリックすると、画面左側からドロワーメニューが表示され、背景ページのスクロールが停止します。「×」ボタン・背景の暗い部分・Escキーのいずれかで閉じると、元の位置からスクロールを再開できます。

背景ページのスクロールを停止する仕組み
今回は、bodyを固定するのではなく、ページ全体を表すhtml要素にクラスを追加してスクロールを停止しています。script.jsのlockScroll()関数では、現在のスクロール位置を保存したあと、classList.add()でscroll-lock-drawer_lockedクラスを追加します。

function lockScroll() {

  // メニューを開いた時点のスクロール位置を保存する
  savedScrollY = window.scrollY;

  // htmlにクラスを追加して背景のスクロールを禁止する
  document.documentElement.classList.add("scroll-lock-drawer_locked");

}

window.scrollYは、ページを上端からどれだけスクロールしたかをピクセル単位で取得するプロパティです。取得した値をsavedScrollYに保存することで、メニューを閉じたときに元の位置へ戻せます。
document.documentElementはhtml要素を表します。追加したクラスに対応するCSSは次のとおりです。

/* メニュー表示中はページ全体のスクロールを禁止する */
html.scroll-lock-drawer_locked {
  overflow: hidden; /* 背景ページのスクロールを停止する */
  scrollbar-gutter: stable; /* スクロールバーの表示領域を確保する */
}

overflow: hidden;によってページ全体のスクロールを禁止しています。scrollbar-gutter: stable;は、スクロールバーが非表示になった場合でも表示領域を確保するための指定です。スクロールバーの有無によるページ幅の変化を抑える目的で使用しています。

メニューを閉じたときにスクロールを再開する方法
unlockScroll()関数では、classList.remove()でスクロール停止用のクラスを削除します。その後、window.scrollTo()を使用して、メニューを開く前に保存したスクロール位置へ戻します。

function unlockScroll() {

  // htmlのクラスを削除してスクロールを再開する
  document.documentElement.classList.remove("scroll-lock-drawer_locked");

  // メニューを開く前のスクロール位置へ戻す
  window.scrollTo(0, savedScrollY);

}

window.scrollTo(0, savedScrollY)の最初の0は横方向のスクロール位置、savedScrollYは保存した縦方向のスクロール位置です。これにより、メニューを閉じたあとも、開く直前の位置からページの閲覧を再開できます。

メニューの開閉と背景クリック・Escキーの操作
メニューを開くときは、openMenu()関数でスクロールを停止し、メニューとオーバーレイにis-activeクラスを追加します。閉じるときは、closeMenu()関数でクラスを削除してスクロールを再開します。

// 背景ページのスクロールを停止する
lockScroll();

// メニューと背景を表示する
drawer.classList.add("is-active");
overlay.classList.add("is-active");

メニューを閉じるときは、次の処理を実行します。

// メニューと背景を非表示にする
drawer.classList.remove("is-active");
overlay.classList.remove("is-active");

// 背景ページのスクロールを再開する
unlockScroll();

背景の暗い部分をクリックした場合も、closeMenu()を実行します。また、keydownイベントでEscキーを検知し、メニューが開いている場合だけ閉じるようにしています。

// 背景クリックでメニューを閉じる
overlay.addEventListener("click", closeMenu);

// Escキーでメニューを閉じる
document.addEventListener("keydown", function(event) {

  if (
    event.key === "Escape" &&
    drawer.classList.contains("is-active")
  ) {
    closeMenu();
  }

});

aria-expandedとaria-hiddenは、メニューの開閉状態を示す属性です。閉じている間はinertによってメニュー内の操作を無効にし、開くときに解除しています。

メニューのデザインと動作を変更する方法
メニューの横幅・最大幅・内側余白は、style.cssの.scroll-lock-drawer_panelで変更できます。

.scroll-lock-drawer_panel {
  width: 320px; /* メニューの横幅 */
  max-width: 85%; /* 画面幅に対する最大幅 */
  padding: 32px 24px; /* 上下32px・左右24pxの余白 */
}

widthの数値を大きくするとメニューが広くなり、小さくすると狭くなります。max-width: 85%;は、画面が狭い場合にメニューが画面幅の85%を超えないようにする指定です。
スライドの速さはtransitionの0.3sで調整できます。閉じた状態ではメニューを左側に隠し、is-activeクラスが追加されると画面内へ移動します。

/* 閉じている状態 */
.scroll-lock-drawer_panel {
  transform: translateX(-100%); /* メニューを左側へ隠す */
  transition: transform 0.3s ease, visibility 0.3s ease; /* スライド速度 */
}

/* 開いている状態 */
.scroll-lock-drawer_panel.is-active {
  transform: translateX(0); /* メニューを画面内へ表示する */
}

0.3sを0.5sに変更すると動きが遅くなり、0.2sに変更すると速くなります。
背景の暗さは、オーバーレイのbackgroundで調整できます。

.scroll-lock-drawer_overlay {
  background: rgba(15, 23, 42, 0.55); /* 背景の色と透明度 */
}

最後の0.55が不透明度です。0に近づけると透明になり、1に近づけると濃くなります。
メニューリンクの文字と移動先は、index.htmlのhref属性とリンク文字列を変更してください。

<!-- メニューリンクの設定例 -->
<a class="scroll-lock-drawer_link" href="/service/">サービス</a>

スマートフォンでの表示調整
style.cssでは、@media (max-width: 640px)を使用して、画面幅が640px以下の場合にメニューの余白・タイトル・リンクの文字サイズを変更しています。

@media (max-width: 640px) {

  /* メニューの内側余白を調整 */
  .scroll-lock-drawer_panel {
    padding: 24px 18px; /* 上下24px・左右18pxの余白 */
  }

  /* メニュータイトルの文字サイズを調整 */
  .scroll-lock-drawer_heading {
    font-size: 22px; /* 文字サイズ */
  }

  /* メニューリンクを調整 */
  .scroll-lock-drawer_link {
    padding: 12px 14px; /* 上下12px・左右14pxの余白 */
    font-size: 14px; /* 文字サイズ */
  }

}

640pxを変更すると、スマートフォン向けのスタイルを適用する画面幅を調整できます。
メニューの高さには100dvhを指定し、スマートフォンの表示領域に合わせています。また、overflow-y: auto;によって、メニュー内の内容が画面の高さを超えた場合も内部だけをスクロールできます。

.scroll-lock-drawer_panel {
  height: 100vh; /* 画面の高さに合わせる */
  height: 100dvh; /* モバイルの表示領域に対応する */
  overflow-y: auto; /* メニュー内部のスクロールを許可する */
  overscroll-behavior: contain; /* スクロール操作が背景へ伝わるのを抑える */
}

こちらもおすすめ


コメント