結論:犯人の要素を特定してから直す。hiddenで隠すのは最後
- 原因の9割は「特定の1要素が画面幅を超えている」だけ=全体のレイアウト崩壊ではない
- まずJSスニペットで幅を超えている要素を一覧に出す(目視より速い)
- よくある犯人は 100vw・長いURLや英数字・はみ出したflex子要素・負のmargin・画面外のアニメーション用要素
- html,bodyへの overflow-x:hidden は position:sticky を巻き添えで殺すので安易に使わない
- どうしても隠すなら hidden ではなく overflow-x:clip を使う
「PCでは問題ないのに、スマホだと画面が少し横に動く」「右にスワイプすると白い余白が出る」——レスポンシブ実装で最後まで残りがちな不具合です。厄介なのは、見た目にはどこがはみ出しているのか分からない点で、犯人探しに時間を溶かしがちです。結論から言うと、先に「はみ出している要素」を機械的に特定してから、その要素だけを直すのが最短です。この記事では、実案件でそのまま使っている特定手順と、原因別の直し方、そして安易な overflow-x:hidden がなぜ危険なのかを整理します。
まず「はみ出している要素」を1発で特定する
横スクロールは、ページ全体が広がっているのではなく、たいてい1つか2つの要素だけが画面幅を超えています。DevToolsで要素を上から順に開いていく方法もありますが、入れ子が深いサイトでは時間がかかります。ブラウザのコンソールに次のスニペットを貼って、超えている要素だけを列挙するのが確実です。
// 画面幅を超えている要素だけをコンソールに出す
(function () {
var limit = document.documentElement.clientWidth;
var hits = [];
document.querySelectorAll('*').forEach(function (el) {
var r = el.getBoundingClientRect();
if (r.right > limit + 1 || r.left < -1) {
hits.push({ el: el, left: Math.round(r.left), right: Math.round(r.right) });
}
});
console.table(hits);
hits.forEach(function (h) { h.el.style.outline = '2px solid red'; });
console.log('over:', hits.length, '/ viewport:', limit);
})();
実行すると、超過している要素がテーブルで並び、同時に赤い枠が付くので画面上でも位置が分かります。ポイントは親要素も一緒に引っかかるので、リストの一番下=もっとも深い子要素が本当の犯人であることが多いことです。上から順に見るとレイアウト全体が悪いように錯覚しますが、実際は末端の1要素だけを直せば解決します。
よくある誤解として * { outline: 1px solid red; } を全要素に当てる方法がありますが、要素が多いページでは枠だらけになって逆に分かりません。実務では上のスニペットのほうが圧倒的に速いです。
スマホ実機でしか出ない場合の確認方法
PCの検証モードでは再現せず実機だけで出る場合は、PCとスマホでスクロールバーの扱いが違うことが原因のことがあります。実機を繋いでリモートデバッグするのが正攻法ですが、手元にケーブルが無いときは、はみ出し幅をページ内に表示させてしまうのが早いです。
// 実機の画面にそのまま数値を出す簡易デバッグ
var d = document.createElement('div');
d.style.cssText = 'position:fixed;left:0;bottom:0;z-index:99999;background:#000;color:#fff;font:12px/1.4 monospace;padding:4px 6px;';
d.textContent = 'scrollW=' + document.documentElement.scrollWidth
+ ' clientW=' + document.documentElement.clientWidth;
document.body.appendChild(d);
scrollWidth が clientWidth より大きければ横スクロールが発生しています。差分がそのまま「何pxはみ出しているか」なので、この数値が原因の見当をつける材料になります。実測では、後述する 100vw 起因なら差分がスクロールバー幅とほぼ一致し、画像や表が原因なら数十〜数百pxとまとまった値になります。
原因1:100vw がスクロールバー幅の分だけはみ出す
「フルワイドの背景を作りたい」場面で width: 100vw を使うのは定番ですが、vw の 100 はスクロールバーを含んだウィンドウ幅です。縦スクロールバーが常時表示される環境(Windowsのデスクトップブラウザが典型)では、その幅ぶんだけ確実にはみ出します。手元のWindows版Chromeで実測すると差分は15px前後で、これが「PCだけ横に動く」ときの典型パターンです。
逆にスマホはスクロールバーがオーバーレイ表示のため差分が出ません。PCで出てスマホで出ない、という切り分けができたらまず 100vw を疑うのが早道です。
/* NG: スクロールバー幅ぶんはみ出す */
.section-full { width: 100vw; }
/* OK-1: 親が画面幅なら 100% で足りる */
.section-full { width: 100%; }
/* OK-2: 中央からフルワイドに広げたい場合 */
.section-full {
width: 100%;
margin-inline: calc(50% - 50vw);
max-width: 100vw;
}
なお OK-2 の書き方も 50vw を使っているため厳密にはスクロールバー幅の影響を受けます。気になる場合は親側に overflow-x: clip を付けて、はみ出しをその親だけで止めるのが安全です(後述)。
原因2:flexやgridの子要素が min-width:auto で縮まない
実案件でいちばん多い犯人がこれです。flexアイテムには初期値として min-width: auto が効いており、中身より小さくなれないという制約があります。中に長いURL、英数字の型番、折り返しのないテーブルなどが入っていると、親の幅を無視して押し広げます。
/* flex の子が縮まない場合 */
.card {
min-width: 0; /* これで中身より小さくなれる */
}
/* grid の場合はトラック側で指定する */
.grid {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
/* 長いURLを折り返す */
.card p {
overflow-wrap: anywhere;
}
ここで注意したいのは、min-width: 0 と overflow-wrap は役割が違うことです。前者は「箱が縮めるようにする」設定、後者は「中身が折り返せるようにする」設定で、長いURLが原因のときは両方必要になります。片方だけ入れて直らず悩むケースをよく見ます。
原因3:画面外に置いた要素が生きている
スライドインするメニュー、スクロール連動で外から入ってくる装飾、閉じている状態のドロワー——これらを transform: translateX(100%) や left: 100% で画面外に逃がしている場合、要素そのものはページ幅に数えられます。閉じているのに横スクロールが出る、という現象はほぼこれです。
対処は、閉じている状態では領域から外すことです。
.drawer {
transform: translateX(100%);
visibility: hidden; /* 領域は残るがフォーカスは奪わない */
}
/* 親側で止める */
.drawer-wrap { overflow-x: clip; }
アニメーションのために display:none にできないケースが多いので、「はみ出しを親でクリップする」+「閉じている間は操作対象から外す」の2点セットで扱うのが実務的です。visibility: hidden にしておくと、閉じたドロワー内のリンクにタブキーでフォーカスが入ってしまう事故も同時に防げます。
原因4:負のmargin・padding込みの幅指定・はみ出す画像や表
残りは次のあたりを順に確認すれば、たいてい当たります。
- 負のmargin:グリッドの隙間調整で
margin-left: -15pxなどを付けた要素が、親の左端を越えている - padding込みの幅:
width: 100%の要素に左右paddingが乗っている(box-sizing: border-boxが効いていない領域が残っている) - 画像:
img { max-width: 100%; height: auto; }が当たっていない箇所(記事本文の外や、後から追加したコンポーネント) - 表:列数の多いテーブル。これは縮めるのではなく
overflow-x: autoの親で包んで、表だけ横スクロールさせるのが正解 - 埋め込み:YouTubeやGoogleマップのiframe。
width属性の固定値が残っていることがある
表と埋め込みだけは「はみ出しを消す」のではなく「その要素の中だけスクロールさせる」のが正しい対処です。ページ全体を横に動かさずに済み、内容も欠けません。
.table-scroll {
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
overflow-x:hidden で隠してはいけない理由
検索すると「html, body { overflow-x: hidden; } で解決」という情報が大量に出てきます。確かに横スクロールは止まりますが、これは原因を消しているのではなく、はみ出したまま見えなくしているだけです。実務では次の副作用を引き当てます。
position: stickyが効かなくなる:祖先にoverflowの hidden/auto/scroll があると、stickyはその祖先内でしか固定されず、実質動かなくなります。追従ヘッダーや目次が突然効かなくなる原因のトップです- ページ内アンカーのスクロール位置がずれる:スクロールの基準となる要素が変わるため、
scroll-behaviorやscroll-margin-topの効きが変わります - 原因が温存される:はみ出した要素は消えていないので、後から別のブレークポイントやコンテンツ量で再発します
どうしても包括的に止めたい場合は、hidden ではなく clip を使います。overflow-x: clip は「はみ出しを切り取るだけでスクロールコンテナを作らない」ため、sticky を巻き添えにしません。
/* 副作用の少ない止め方 */
body {
overflow-x: clip;
}
/* さらに範囲を絞るなら、はみ出す親だけに当てる */
.hero-wrap { overflow-x: clip; }
ただし clip は縦方向を visible のまま残せる代わりに、その要素より内側では position:fixed の基準が変わる場合がある点に注意してください。既存サイトへ後から入れるときは、追従ヘッダーとモーダルの動作を必ず確認します。順序としては「①犯人を特定して直す → ②どうしても残るなら該当する親だけ clip → ③body全体の clip は最後の手段」です。hidden は選択肢から外して構いません。
再発させないためのチェックリスト
公開前とコンポーネント追加時に、この5項目だけ確認しておくと横スクロールはほぼ出なくなります。
- コンソールのスニペットで超過要素が0件であることを、375px幅と横向き(812px前後)の両方で確認する
- 新規に
100vwを書いていないか(レビュー時にgrepする) - flex・gridの子に長い文字列が入る可能性がある箇所に
min-width: 0とoverflow-wrapがあるか - テーブルと埋め込みは、スクロール用のラッパで包まれているか
- 閉じているドロワー・スライドイン要素が、親でクリップされているか
横スクロールは「見た目の小さな不具合」に見えますが、スマホで指が横に滑るとレイアウトが破綻して見え、そのまま離脱につながります。特にお問い合わせ導線の手前で起きると機会損失に直結するため、公開前チェックに入れておく価値のある項目です。
関連記事
- 多言語サイトの作り方完全ガイド|WordPress多言語対応の手順・費用・外注の判断基準(言語によって文字列長が変わり、はみ出しが再発しやすい点も解説)
- WordPressのサーバー移行完全ガイド(移行後にテーマCSSが差し替わって崩れたときの確認手順)
- Web制作者が選ぶレンタルサーバー比較(表示速度まわりを見直すときの選定基準)
多言語・ベトナム語サイトの制作でお困りではありませんか?
フォント・文字化け・WordPress多言語化・サーバー選定まで、多言語サイト制作の実績を持つ制作会社が一括対応します。お見積り・ご相談は無料です。
多言語サイト制作の詳細を見る →すぐに相談したい方は お問い合わせフォームへ
関連記事(コーディング実務Tips)
- 100vhがスマホでずれる原因とdvh/svh/lvhの使い分け
- 全画像のlazyでLCPが悪化する理由と外す画像の見分け方
- position:stickyが効かない7つの原因
- flexアイテムが縮まない・ellipsisが効かない原因はmin-width:auto
- position:fixedが効かない原因と直し方|親のtransform/filterが包含ブロックを奪う
- object-fitとaspect-ratioが効かない・画像の縦横比が崩れる原因と直し方
- iPhoneで入力欄をタップすると画面が拡大する原因と直し方|16px指定とuser-scalableの扱い
- scroll-behavior: smoothが効かない原因10パターン|スムーススクロールが滑らない/滑りすぎる原因の実測版
- 日本語の改行がおかしい・URLがはみ出す原因