モーダルとかオーバーレイ表示した時に、背景のスクロール止めたいですよね。
普通は overflow: hidden で止まるんですが、iOSだとこれが効かない!!
実案件でめちゃくちゃハマったので、原因と解決策をまとめました🐧
まず結論
overflow: hidden ではなく、position: fixed で body を固定するのが正解です!!
let savedScrollY = 0;
// スクロールロック
function lockScroll() {
savedScrollY = window.scrollY;
document.body.style.overflow = 'hidden';
document.body.style.position = 'fixed';
document.body.style.top = `-${savedScrollY}px`;
document.body.style.width = '100%';
}
// スクロール解除
function unlockScroll() {
document.body.style.removeProperty('overflow');
document.body.style.removeProperty('position');
document.body.style.removeProperty('top');
document.body.style.removeProperty('width');
window.scrollTo(0, savedScrollY);
}
これでiOSでもPCでも確実にスクロールが止まります!!
何が起きたか
サイトのトップに言語選択モーダルを実装してたんですが、PCでは問題なく動いてました。
が、iPhoneで確認したらモーダルの後ろがスクロールできちゃう…!!
overflow: hidden はちゃんとかけてるのに、です。
「え、なんで…?」ってなりました。
原因:WebKit がタッチスクロールに overflow: hidden を適用しない
調べたら、WebKit Bug #153852 として報告されている既知のバグでした。
そして重要なのが、Safari だけの話じゃないということ!!
iOSではAppleの制約で、Chrome も Firefox も Edge も全部 WebKit エンジンを使っています。
なのでiOS上のどのブラウザでも同じ問題が起きます。
実際に iPhone の Chrome でも再現しました。
Safari だけかと思ったら全ブラウザだったので焦りました…!!
なぜ position: fixed で解決できるのか
position: fixed をかけると、body がビューポートに固定されてドキュメントフローから外れます。
つまりスクロールする対象自体がなくなるので、iOSでも確実に止まります!!
ポイントは3つ:
- ✅ ロック時に
window.scrollYを保存してtop: -Npxで見た目のズレを防ぐ - ✅
width: 100%を忘れない(忘れると body の幅が潰れる) - ✅ 解除時に
scrollTo()で元の位置に戻す(忘れるとページトップに飛ぶ)
やりがちなNG実装
自分が試してダメだったパターンも晒しておきます!!
| やったこと | 結果 |
|---|---|
body { overflow: hidden } | ❌ iOSのタッチスクロールに効かない |
html { overflow: hidden } | ❌ 同じく効かない |
touch-action: none | ❌ 一部ブラウザで効かない |
全部試した上で position: fixed に辿り着きました。
最初からこれやっとけばよかった…!!
まとめ
- iOS では
overflow: hiddenだけではタッチスクロールが止まらない - Safari だけじゃなく iOS の Chrome や Firefox でも発生する(全部 WebKit だから)
position: fixed+ スクロール位置の保存・復元が一番確実!!
モーダル実装する時はとりあえずこの方法でやっていきましょー!!🐧
参考
- 【2024】iOSでモーダル表示時に背面のスクロールを固定・抑制する
- 【iOS16】iOSにおけるモーダルウィンドウの背景固定&下までスクロールできない問題の解決方法
- WebKit Bug #153852
以上!!
誰かのお役に立てれば嬉しいです🐧
