概要
Webサイト制作において、ハンバーガーメニューの実装は避けて通れないタスクですが、多くの初心者が直面する「落とし穴」があります。それが、メニューを開いた状態で画面をスクロールすると、背後にあるコンテンツまで一緒に動いてしまうという現象です。これはUX(ユーザー体験)の観点から非常に好ましくありません。
本記事では、この「背景スクロールの固定(Body Scroll Locking)」を、モダンなWeb開発の現場で採用されている最も堅牢で軽量な手法を用いて解説します。ライブラリに頼らず、CSSと数行のJavaScriptで解決する方法を深掘りしていきましょう。
なぜハンバーガーメニューで背景がスクロールするのか
ブラウザのデフォルト挙動として、body要素にはスクロールイベントが紐付いています。ハンバーガーメニューを開いた際、そのメニュー要素に`overflow: scroll`や`overflow-y: auto`を指定したとしても、ブラウザの解釈としては「bodyのスクロール」が優先されるか、あるいはメニュー内のスクロールとbodyのスクロールが連動してしまう(バブリング現象)ことが原因です。
これを防ぐためには、単にスタイルを当てるだけでなく、メニューが開いている間、body自体のスクロール機能を「無効化」するロジックが必要不可欠となります。
アプローチ1:CSSでbodyの固定を実現する
最も直感的かつ効果的な方法は、メニューオープン時にbodyに対して特定のクラスを付与し、そのクラスに`overflow: hidden`を適用する方法です。
/* CSS */
.is-fixed {
overflow: hidden;
height: 100vh;
}
この方法はシンプルですが、一つ大きな弱点があります。それは、`overflow: hidden`を適用した瞬間にスクロールバーが消失し、画面幅がわずかに広がることでコンテンツがガタつく(レイアウトシフト)問題です。これを防ぐためには、スクロールバーの幅を考慮したパディング調整が必要になります。
アプローチ2:JavaScriptによる制御とレイアウトシフトの回避
実務では、単なる`overflow: hidden`だけでは不十分です。以下のJavaScriptコードは、スクロールバーの幅を計算し、レイアウトシフトを完全に防ぐためのプロフェッショナルな実装例です。
// JavaScript
const body = document.body;
const hamburger = document.querySelector('.hamburger-btn');
const menu = document.querySelector('.menu-panel');
function toggleScrollLock(isOpen) {
if (isOpen) {
const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
body.style.paddingRight = `${scrollbarWidth}px`;
body.classList.add('is-fixed');
} else {
body.style.paddingRight = '';
body.classList.remove('is-fixed');
}
}
hamburger.addEventListener('click', () => {
const isOpen = menu.classList.toggle('is-active');
toggleScrollLock(isOpen);
});
このコードの肝は、`window.innerWidth`(スクロールバー込みの幅)と`document.documentElement.clientWidth`(スクロールバーを除いた幅)の差分を計算し、その分を`padding-right`としてbodyに与える点です。これにより、スクロールバーが消えても画面上のコンテンツが左右に揺れることを防げます。
メニュー内のみをスクロールさせるCSSの構成
メニュー自体が長いコンテンツを含む場合、以下のCSS設定が必須です。
/* メニューパネルのCSS */
.menu-panel {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
overflow-y: auto; /* ここが重要 */
-webkit-overflow-scrolling: touch; /* iOSでの慣性スクロール用 */
}
特にiOS(Safari)環境では、`overflow-y: auto`だけではスクロールがカクつくことがあります。`-webkit-overflow-scrolling: touch`を指定することで、ネイティブアプリのような滑らかなスクロール感を実現できます。
実務アドバイス:なぜライブラリではなく自前実装を推奨するのか
「body-scroll-lock」のような著名なライブラリも存在しますが、小規模なプロジェクトやLP制作において、わざわざ外部依存を増やすのは保守性の観点からリスクになる場合があります。
今回紹介したコードは、依存関係が一切なく、モダンブラウザであれば確実に動作します。また、実務において重要なのは「モーダル」や「検索オーバーレイ」でも同じロジックを使い回せるように、関数化(モジュール化)しておくことです。
また、もしメニュー内でさらに子階層がある場合、`overscroll-behavior: contain`プロパティをメニューパネルに付与してください。これにより、メニュー内でスクロールが末端に達した際に、親要素(背後のサイト)がスクロールされてしまう「スクロールチェーン」をCSSだけで抑制できます。これは最新のWeb開発において非常に強力なツールです。
アクセシビリティの観点から注意すべきこと
背景を固定する際、キーボード操作(Tabキー)にも配慮が必要です。メニューが開いているのに、裏側のリンクにフォーカスが移動できてしまうと、スクリーンリーダー利用者は混乱します。
メニューが開いたタイミングで、背後の要素(mainタグなど)に`aria-hidden=”true”`を付与し、メニュー内の最初の要素にフォーカスを強制的に移動させる「フォーカストラップ」の実装も、シニアデザイナーとしては考慮に入れるべき高度なタスクです。
まとめ
ハンバーガーメニューの背景スクロール制御は、以下の3つのポイントに集約されます。
1. bodyへの`overflow: hidden`適用によるスクロールの無効化
2. スクロールバーの幅を計算し、`padding-right`でレイアウトシフトを相殺する
3. `overscroll-behavior: contain`を用いてスクロールチェーンを遮断する
これらを実装することで、ユーザーはメニュー操作に集中でき、Webサイト全体の品質が一段階引き上がります。CSSの機能とJavaScriptの細やかな制御を組み合わせ、快適なUIを提供しましょう。Webデザインの本質は、ユーザーがストレスを感じる「違和感」を一つずつ潰していく作業に他なりません。本記事のコードをテンプレートとして、ぜひ現場で活用してください。

コメント