こんにちは、ナカムラです。
だいぶ前に階層の深いハンバーガーメニューの作り方をご紹介しました。
こちらの記事では開閉の処理について書きました。
今回は、メニューを開いた時のスクロールの制御について書きたいと思います。
今回のポイント
- メニューを開いている時に、メニューの下のメインコンテンツ(body)をスクロールさせない
- メニューを閉じた時に、開いた時の表示位置に戻す
DEMO
See the Pen javascriptでbodyのスクロールを禁止するハンバーガーメニューを作る by Nakamura (@takayo-nakamura) on CodePen.
基本の開閉は「階層の深いハンバーガーメニュー」と同じです。
追加した部分を解説していきます。
開く時の処理
まずは現在地を取得します。これは閉じる時にも使いますので変数に入れておきます。
次に、htmlタグにメニューが開いたことを示すクラス「is-menuOpen」を付与します。
CSSの方にis-menuOpenを付与された時のスタイルを追加しています。
positionをfixedにすることで「メニューの下のメインコンテンツ(body)をスクロールさせない」を実現します。
body{ .is-menuOpen & { position: fixed; top: 0; left: 0; width: 100%; height: 100vh; } }
ただ、そのままではひとつ問題があります。
ある程度スクロールしたときに、メニューを開くと、一瞬一番上の表示がチラつくようになります。
そのため、開いた時も、スクロール位置を維持するために、
さきほど取得しておいたスクロール位置をbodyのtopへ付与します。
これで開く時のチラツキを解消できます。
(どういう現象かわからない場合は、body.style.top = scrollpos * -1 + 'px'
を消して操作してみてください。)
あとはメニューのheightがデフォルトで0なので、
「ウィンドウの高さ - ヘッダーの高さ」を付与することでメニューが開きます。
(ここはCSSで処理しても良いですが、スマホの場合はアドレスバーの高さの考慮なども必要になります)
// 現在のスクロール位置を取得する scrollpos = window.pageYOffset // メニューが開いたことを示すクラスをhtmlに付与する html.classList.add('is-menuOpen') // bodyのtopにスクロール位置を付与する document.body.style.top = scrollpos * -1 + 'px' // ウィンドウの高さを取得 bodyHeight = window.innerHeight // 取得した高さを、メニューに付与する(ヘッダーの高さを引いた数) menu.style.height = bodyHeight - headerHeight + 'px'
閉じる時の処理
閉じる時は、開く時に付与したものをはずしていくイメージです。
is-menuOpenをはずしたので、bodyは通常の状態にもどります。
スクロール位置がずれてしまいますので、window.scrollTo(0, scrollpos)
で取得しておいた位置へ移動させます。
最後に、メニューの高さを0にもどせばメニューを閉じる処理の完了です。
// メニューが開いたことを示すクラスをはずす html.classList.remove('is-menuOpen') // スクロール位置を開いた時の位置へ戻す window.scrollTo(0, scrollpos) //メニューを閉じる(高さを0にする) menu.style.height = 0
最後に
メニューの階層が深い場合は、「JavaScriptで階層の深いハンバーガーメニューを作る」でご紹介した処理を組み合わせてみてください。
メニューのボリュームによってはメニュー内でスクロールできるように、CSSでoverflow:auto;
を追加してください。
ハンバーガーメニューはやることがいっぱいですね。
追記
iOS15以降のアドレスバー問題
スクロールしてアドレスバーが小さくなった時に、bodyの高さがそれに準じない問題があるかと思います。
htmlにmin-height: 100vh;で解決しました。
▼新しい単位で、jsもいらないかも(2022/10/9追記) coliss.com
▼そもそもvhとかいらないかもしれない(2022/11/2追記)
今更ですが、高さいっぱいのメニューなら、bottomも0にすれば勝手に吸着しますよね…vhとかlvhとかいらなかったかもなと思ったりしています。
(後日、記事の内容を見直しますね)
top: 0; bottom: 0;
CSSで解決する方法
下記の記事で、bodyの固定をせずにスクロールの問題を解決する方法(CSS)が紹介されています。
※safariがまだ対応していないので、結局まだjsが必要そうですが、そのうち対応されるんじゃないでしょうか。
overscroll-behavior: none;
対応状況 caniuse.com
2023/2/2 追記:iOS safariでも機能してました。もうこれでいいですね。