スマホだけ横にちょっとスクロールできる現象を直す方法(CSS overflow-x clip)
概要
今回はサイトを作っているときにスマホでだけ左右にほんの少しスクロールできてしまう現象(iOS Safariのelastic bounce)の直し方について紹介していきます。
サイト開発をしていると、PCではちゃんと動作しているのに、スマホで触ったときだけ「左右にちょっとだけスクロールできて、指を離すとスーッと中央に戻る」というな謎な挙動に出くわすことってありますよね(- -;
壊れているわけではないけれど、触ったときの違和感が大きい。
レイアウトが崩れているわけではないので、ぱっと見では原因が分からず、説明も難しい、、、
これは結構ハマるやつです。
自分はハマりました。
同じ症状で困っている方の参考になればと思って残しておきます(^^b
それではやっていきましょう(^^!
具体的なソースを一応記載しているので、是非試してみてください!
目次
何が起きていたか(症状)
まず症状から整理します。
- スマホでサイトを表示すると、左右にほんの数pxだけスクロールできてしまう
- 指を離すとスーッと中央に戻る(=本来は固定で居てほしい状態)
- PCで見たときは何も起きない(横スクロールなんてできない)
- DevToolsのスマホエミュレートではこんな操作しないので、気づきにくい
ぱっと見「右側がはみ出してる?」と思うのですが、実際にスクロールしてみると左にも右にもバウンスするので「両方スクロールできるってどういうこと?」となります。
ここがちょっと厄介で、原因の見立てを誤るとかなり時間を溶かします(- -;
実際これってどう調べたらいいのかわからないので、本当に沼るんですよね…
なぜ起こるのか — iOS Safariのelastic bounce
結論から言うと、iOS Safariはページの中身がviewportより少しでも広いと、両方向に弾むスクロール(elastic bounce)を許可します。
たとえ右に2pxはみ出しているだけでも、左にも右にもバウンスできてしまうのです。
つまり「両方スクロールできるように見える」状態は、実際には片側だけが数pxはみ出している、というケースがほとんどです。
はみ出しの原因になりやすいパターン
経験上、犯人になりやすいのは以下です。
- 装飾要素を
right: -20pxのように負の値で配置している(吹き出し、リボン、影など) position: absoluteの浮遊アイコンをleft: 0などコンテナの端ピッタリに置いているdrop-shadowやbox-shadowがコンテナの外にはみ出している- 画像やSVGが想定より大きく描画されている
PCではグリッドの隙間や余白に吸収されて気づきません。
スマホで1カラムになった瞬間、装飾がviewport端を数pxだけ突き抜けて発覚する、というのが定番パターンですね。
ちなみに装飾を後から足したタイミングで再発しがちなので、「以前は出てなかったのにいつの間にか出るようになった」というケースも多いです。
直し方:親セクションに overflow-x: clip; を1行
直すのは簡単で、はみ出しを許容している親セクションに overflow-x: clip; を1行足すことです。
style.css
1 | .hero { |
これだけです。
ポイントはhidden ではなく clip を使うこと。
ここを hidden にしてしまうと、別の副作用(後述)を引き起こす可能性があるので注意してください。
| プロパティ | スクロールコンテナを作る? | sticky への影響 |
|---|---|---|
overflow-x: hidden |
作る | position: sticky が効かなくなることがある |
overflow-x: clip |
作らない | sticky もそのまま動く |
ここが大きな違いです。
overflow-x: hidden はその要素を「スクロールコンテナ」として扱ってしまうため、子孫要素の position: sticky が想定通り動かなくなることがあります。
一方で clip は単純に「はみ出した分を視覚的に切る」だけで、内部のレイアウトには手を加えません。
つまりstickyヘッダーやサイドバーなどがあるサイトでは、hidden ではなく clip を選ぶのが安全です。
ちなみにoverflow-x: clip はSafari 16以降を含むモダンブラウザはすべて対応しているので、今となってはほぼ気にせず使えます(^^b
どこに当てるか — html, body への適用は避けるべき?
便利な overflow-x: clip ですが、html, body に当てるのは横着なのでやめましょう。
理由は単純で、サイト全体に影響するため、思いもよらない動作を引き起こす可能性があるからです。
たとえば後から「画面端からふわっと出てくるアニメーション」みたいなものを実装したくなったとき、大元で overflow-x: clip がかかっていると意図せず切られてしまう、ということが起きます。
こういったときはなんでだ?とかなり調査に時間がかかることが多いので、脳死でhtmlやbodyに適用するのは危険だと思います。
正解:問題が起きているセクション単位で当てる
問題が起きているセクションだけに overflow-x: clip を当てるのが正解です。
今回のケースでは、ファーストビュー全体を包むセクションに1行足すだけで解決しました。
上でも言ったようにメリットは下記のとおりです。
- 影響範囲を最小限にできる
- 他のページや将来の装飾に影響しない
- 装飾を後から増やしても再発しにくい
- どこで何を抑え込んでいるかコードを読めば分かる
装飾要素の位置を一つずつ手直しするより、親側で受けるほうがコードもシンプルで、後から装飾を増やしても再発しません。
これが今回一番伝えたいポイントです(^^
まとめ
今回のポイントを整理しておきます。
- 「スマホでだけ左右にちょっとスクロールできる」現象は、たいてい数pxのはみ出しが原因
- 犯人は装飾要素の負の
positionやbox-shadowのはみ出しが多い - 直し方は親セクションに
overflow-x: clip;を1行足すのが最短で副作用も少ない hiddenではなくclipを選ぶ(sticky を壊さないため)html, bodyへの一括適用ではなく、問題が起きているセクション単位で当てる
特に最後の「セクション単位で当てる」は、後から装飾を足したときの再発防止にもなるので意識しておくと楽です(^^b
CSSは「グローバルに当てる方が手っ取り早く感じる」場面が多いですが、影響範囲を狭く保つほうが結局メンテが楽になる、というのはよくある話ですね。
以上となります。
スマホ実機で確認してみないと気付かないバグは多いので、リリース前に一度触ってみるのをお勧めします!
それではお疲れ様です!