「ヘッダーを position: sticky で固定したのに、スクロールしても追従しない」「昨日まで動いていたサイドバーが、横スクロール対策を入れた途端に固まらなくなった」——CSS を触っていると、こうした場面に出くわします。
position: sticky は「一番近いスクロールする祖先」を基準に動きます。ところが祖先に overflow-x: hidden を付けると、その祖先が意図せず「スクロールする箱」になり、基準がずれてしまいます。この記事では、原因の見つけ方と、見た目を変えずに直す overflow-x: clip への置き換えを、コピペで試せる形で整理します。
-
html や body に overflow-x: hidden を付けると position: sticky が効かなくなる - 原因は、祖先が「スクロールコンテナ」になり sticky の基準が変わること
- 見た目を変えたくないなら
overflow-x: hidden を overflow-x: clip に置き換える - コンソールのスクリプトで、
overflow が visible でない祖先を洗い出せる 目次を開く
position: sticky の要素は、一番近い「スクロールコンテナ」の中で位置が固定されます。スクロールコンテナとは、overflow が visible 以外の値になっている祖先要素のことです。
横方向のはみ出しを隠したくて、次のように書くことがよくあります。
html,
body {
overflow-x: hidden;
} このとき、overflow-x だけを hidden にしても、もう一方の overflow-y は visible のままにはならず、auto として扱われます。結果として body が「縦にもスクロールしうる箱」になり、sticky の基準がウィンドウではなく body になります。body 自体は高さいっぱいに伸びているためスクロールが起きず、sticky が追従しないように見えます。
原因の祖先を見つける
どの祖先が犯人かは、ブラウザの開発者ツールのコンソールで調べられます。sticky が効かない要素を「要素」タブで選んだ状態で、次を実行します。
// $0 は要素タブで選択中の要素
let el = $0.parentElement;
while (el) {
const s = getComputedStyle(el);
if (s.overflowX !== 'visible' || s.overflowY !== 'visible') {
console.log(el, s.overflowX, s.overflowY);
}
el = el.parentElement;
} 出力された要素が、スクロールコンテナになっている祖先です。html や body、レイアウト用のラッパーが並ぶことが多く、どれかに overflow-x: hidden が付いています。
直し方1:overflow-x: clip に置き換える
見た目(はみ出しを隠す)を変えずに直すなら、hidden を clip に置き換えます。clip ははみ出しを切り取るだけで、スクロールコンテナを作りません。
html,
body {
overflow-x: clip;
} 対応ブラウザの目安
| 値 | はみ出しを隠す | スクロールコンテナになる | sticky への影響 |
|---|---|---|---|
hidden | する | なる | 効かなくなる |
clip | する | ならない | 影響しない |
auto | しない(スクロールバーが出る) | なる | 効かなくなる |
clip は比較的新しい値なので、古いブラウザも対象にする場合は、先に hidden を書き、後ろに clip を書いて上書きします。
html,
body {
overflow-x: hidden; /* 古いブラウザ向け */
overflow-x: clip; /* 対応ブラウザではこちらが有効 */
} 直し方2:はみ出しの原因そのものを消す
そもそも横スクロールが出る原因(幅が 100vw の要素、はみ出す画像や表など)を直せば、overflow-x を指定する必要がなくなります。
-
width: 100vwをやめてwidth: 100%にする(縦スクロールバーの幅の分だけはみ出すため) - 画像に
max-width: 100%を付ける - 長い文字列に
overflow-wrap: anywhereを付ける
「とりあえず overflow-x: hidden」を外せるなら、それが最も安全です。
それでも sticky が効かない時のチェックリスト
overflow を直しても動かない場合は、次を上から確認します。
- sticky の要素に
top: 0のような値を指定しているか(指定が無いと固定されない) - 親要素の高さが sticky 要素と同じで、動ける余白が無くなっていないか
- フレックスやグリッドの子の場合、
align-self: startを付けて高さが引き伸ばされていないか -
overflowがvisibleでない祖先が他に残っていないか(上のコンソールスクリプトで再確認)
まとめ
-
position: stickyは、一番近いスクロールコンテナを基準に動く - 祖先の
overflow-x: hiddenは、その祖先をスクロールコンテナにしてしまい、sticky を効かなくする - 見た目を保ったまま直すなら
overflow-x: clipに置き換える - 原因の祖先はコンソールのスクリプトで洗い出せる
よくある質問(FAQ)
overflow-x: clip はどのブラウザで使えますか?
主要ブラウザの比較的新しい版で使えます。古い版も対象にする場合は、hidden を先に書いて clip を後ろに書く二段書きにしておくと、対応していない環境では hidden の動きに戻ります。手元の対象ブラウザで動作確認をしてください。
はい。html に付けた場合も、ブラウザによっては基準がずれて sticky が効かなくなることがあります。まずコンソールのスクリプトで実際にどの祖先が原因かを確認するのが確実です。
overflow-y: visible を一緒に指定すれば直りますか?
直りません。片方が hidden や auto のとき、もう片方の visible は auto として扱われるため、スクロールコンテナ化は避けられません。clip への置き換えが有効です。
sticky ではなく position: fixed に変えるのは有効ですか?
固定はできますが、要素が通常の配置から外れて場所を取らなくなるため、レイアウトを組み直す必要があります。まずは overflow の見直しで解決できるか試すほうが、変更が小さく済みます。
