症状で探す
困っている症状を入力するか、分類で絞り込んでください。 詳しい解説は各記事にあります。
確定版のベースCSS
3本の記事で得た対処を、そのまま使える形にまとめたものです。 自分のサイトでは、Chrome(Android)と Safari(iPadOS)の実機で確認しています。
html {
overflow-x: hidden; /* 想定外のはみ出しを隠す保険 */
overscroll-behavior-x: none; /* iOS の左右の揺れを止める */
}
body {
will-change: transform; /* 合成レイヤーへ昇格 */
position: relative; /* ::before の基準にする */
overflow-x: clip; /* sticky を壊さずクリップ */
z-index: 0; /* 重ね合わせ文脈をつくる */
-webkit-text-size-adjust: 100%; /* iOS の文字拡大を防ぐ */
}
/* 背景の模様は擬似要素へ逃がす */
body::before {
content: "";
position: absolute;
inset: 0;
z-index: -1;
pointer-events: none;
background-image: ...;
background-size: 44px 44px;
}
要素側の原則
/* 横スクロールさせたい箱 */ .scroll-box { overflow-x: auto; max-width: 100%; min-width: 0; /* 縮む余地 */ -webkit-overflow-scrolling: touch; } /* 中身がブロック要素で、折り返さない場合だけ */ .scroll-box > .line { width: max-content; min-width: 100%; white-space: pre; } /* 縦だけスクロールさせたい箱 */ .list-box { max-height: 230px; overflow-y: auto; overflow-x: clip; /* 横も必ず明示する */ min-width: 0; } /* フレックスの子で省略表示したい */ .flex-parent > .ellipsis { min-width: 0; /* これが無いと効かない */ overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
will-change は常時メモリを確保します。
overflow-x: clip は未対応の環境では無視されます。
html の overflow-x: hidden は、
原因を消したうえでの保険として置いています。
どれも副作用があるので、症状が出ていないなら入れる必要はありません。
はみ出し要素を特定する
横スクロールが発生しているとき、どの要素が原因かを調べるスクリプトです。 開発者ツールのコンソールに貼り付けて実行します。
html と body の overflow-x: hidden を
一時的に無効にしてください。隠れていると検出できません。
(function(){ var de = document.documentElement, vw = de.clientWidth; console.log('画面幅: ' + vw + ' 文書幅: ' + de.scrollWidth); // 祖先のどこかで横がクリップされていれば、ページ幅には響かない function contained(el){ for (var p = el.parentElement; p && p !== de; p = p.parentElement) if (getComputedStyle(p).overflowX !== 'visible') return true; return false; } var hits = []; document.querySelectorAll('body *').forEach(function(el){ var r = el.getBoundingClientRect(); if (r.width === 0 && r.height === 0) return; if (getComputedStyle(el).position === 'fixed') return; if (r.right <= vw + 1 && r.left >= -1) return; if (contained(el)) return; hits.push({ el: el, tag: el.tagName.toLowerCase(), cls: el.className && typeof el.className === 'string' ? '.' + el.className.trim().split(/\s+/).join('.') : '', over: Math.round(r.right - vw) }); }); // 祖先を除き、末端だけ残す var leaves = hits.filter(function(h){ return !hits.some(function(o){ return o.el !== h.el && h.el.contains(o.el); }); }); console.log('はみ出し: ' + leaves.length + '件'); console.table(leaves.map(function(h){ return { タグ:h.tag, クラス:h.cls, 超過:h.over }; })); leaves.slice(0,3).forEach(function(h,i){ h.el.style.outline = '3px solid ' + ['red','orange','yellow'][i]; }); })();
background-image)、影、transform、アウトラインなど。
background-image: none を一時的に入れて症状が変わるか確かめるのが早道です。
③の記事で扱っています。
埋まらなかった広告枠
3本の記事を書き終えたあとで、本当の原因が見つかりました。 スクロール中に右へずれる症状は、CSS ではなく 配信されなかった広告枠が起こしていました。
規則性で確定した
決め手は、リロードを繰り返すうちに気づいた規則でした。
| 広告の状態 | 症状 |
|---|---|
| 3カ所すべて表示された | 出ない |
| 2カ所だけ表示(1カ所が空白) | 出る |
| 3カ所すべて削除した | 出ない |
data-ad-status="unfilled" が付き、
中身が空のまま幅を持って残ります。
表示されないので目視では気づけません。
対処
/* 配信されなかった広告枠を消す */ ins.adsbygoogle[data-ad-status="unfilled"] { display: none !important; } /* 高さが確定するまでの1秒ほどを予約しておく */ aside.ad-wrapper { width: 100%; max-width: 100%; min-height: 200px; /* 実際の広告高さに合わせる */ overflow: hidden; contain: layout; }
contain の値に注意。
有効なのは none / strict / content / size / inline-size / layout / style / paint です。
無効な値が混ざると、宣言全体が無視されます。
なお高さの予約は min-height が本体なので、
contain が無くても目的は果たせます。
開発環境で再現しなかった理由
この症状を長く追えなかったのは、確認手段が2つとも「広告のないページ」だったからです。
| 環境 | 広告 | 症状 |
|---|---|---|
| ローカルのファイル | 配信されない | 出ない |
| localhost のプレビュー | 配信されない | 出ない |
| 本番サーバー | 配信される | 出る |
直したのに、直らなかった理由
症状はひとつに見えましたが、原因は3層に重なっていました。
| 層 | 内容 | 結果 |
|---|---|---|
| ① CSS の不備 | min-width:0 欠如、overflow-y 単独指定 | 実在した。直して正解 |
| ② 初回の計算 | 読み込み直後のビューポート幅が不完全 | 再計算で回避 |
| ③ 空の広告枠 | 未配信の枠が幅を持って残る | 真因 |
①を直したとき「解消した」と見えたのは、おそらく 広告の配信状況がたまたま変わったからです。だから再発した。 複数の原因が重なると、部分的な対処が効いたり効かなかったりします。 「直った」の判定そのものが難しくなる。
真因が分かってから、対処を外す
③を潰したあとで、それまでの対処を再検証しました。
| 対処 | 判定 |
|---|---|
| 未配信の枠を畳む | 必須 |
背景を ::before へ逃がす | 必要 |
要素の min-width:0 / max-width:100% | 必要 |
.ad-wrapper で高さを予約 | 必要 |
html { overflow-x: hidden } | 保険として残す |
will-change: transform | 不要 |
load 後の reflow スクリプト | 不要 |
will-change は常時メモリを確保し、
reflow スクリプトは毎回走ります。
症状が消えたなら、対処も消すべきでした。
切り分けの順序
3本を書き終えて、この順で調べればよかったと思う手順です。 実際には推測から入って、何度も遠回りしました。
おすすめの順序
- 1. 症状を確定させる — どの画面幅で、いつ(読み込み直後か、スクロール中か)出るか。
- 2. 診断スクリプトを実行 —
overflow-x:hiddenを外した状態で。 - 3. 出た要素を直す — たいてい
min-width:0かmax-width:100%。 - 4. 0件なら、消して試す — 背景・影・transform を順に無効化する。
- 5. 効果はキャッシュを消して判定 — 「無意味な記述で直った」は、更新されただけかもしれない。
- 6. 複数環境で確認 — Chrome と Safari で有効な対処が違うことがある。
- 7. 本番だけで出るなら、本番だけにあるものを疑う — 広告、計測タグ、遅延読み込み。
- 8. 真因が分かったら、途中の対処を外す — 効いて見えただけのものが混ざっている。