ドロワーメニューを開いている間、背景ページのスクロールを停止する方法を紹介します。通常のドロワーメニューにスクロール制御を加え、メニューの操作中に背後のページが上下へ動かないようにします。
「メニューを開く」ボタンをクリックすると、画面左側からドロワーメニューがスライドして表示され、同時に背景ページのスクロールが停止します。マウスホイールやスマートフォンのスワイプで背景を動かそうとしても、メニューを開いた位置が維持されます。
メニューは右上の「×」ボタン、背景のクリック、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 ダウンロードしたけど、これをネット上で動かすにはどうするの?
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; /* スクロール操作が背景へ伝わるのを抑える */
}
コメント