この記事の目次
position: stickyが効かない原因とは
position: sticky は、通常はページの流れの中にいて、スクロールして指定した位置に達したら、画面に貼り付く配置です。追従ヘッダー、目次、サイドバーに使えます。JavaScript を使わずに実装できますが、「書いたのに効かない」ことが多く、原因は決まっています。
基本(構文・仕組み)
MDN によると、sticky の条件は次のとおりです。
topbottomleftrightのいずれかに、auto 以外の値が必要です。- 基準は、最も近いスクロール祖先(
overflowがhiddenscrollautoの要素など)です。 - 動ける範囲は、親(包含ブロック)の中に限られます。親の端まで来ると、親と一緒にスクロールして消えます。
- sticky は常に新しい stacking context を作ります。CSSのz-indexプロパティによる重なり順は『箱』を基準に決まる と関係します。
/* 追従ヘッダー */
.hdr { position: sticky; top: 0; }
/* 追従サイドバー(Flexbox の2カラム) */
.wrap { display: flex; gap: 20px; }
.main { flex: 1; }
.side {
width: 200px;
align-self: flex-start; /* ← stretch のままだと効かない */
position: sticky;
top: 20px;
}
Flexbox・Grid の子の初期値は、align-items: stretch です。サイドバーが親と同じ高さに引き伸ばされると、動く余地がなくなり、貼り付きません。align-self: flex-start(Grid では align-self: start)が必要です。
サンプルコード(実行結果つき)
<style>
.hdr { position: sticky; top: 0; background: #333; color: #fff; padding: 10px; }
.wrap { display: flex; gap: 20px; padding: 20px; }
.main { flex: 1; height: 2000px; }
.side { width: 200px; height: 100px; align-self: flex-start; position: sticky; top: 20px; }
.side.bad { align-self: stretch; height: auto; }
.ov { overflow: hidden; }
</style>
<div class="hdr">header</div>
<div class="wrap"><div class="main"></div><aside class="side" id="ok">OK</aside></div>
<div class="wrap"><div class="main" style="height:600px"></div><aside class="side bad" id="bad">stretch</aside></div>
<div class="ov"><div class="wrap"><div class="main"></div><aside class="side" id="ov">overflow</aside></div></div>
動作確認(検証環境と結果)
- 環境: Windows 11 / Microsoft Edge 154(Chromium系、headless)/
scrollTo(0, 500)のあとにgetBoundingClientRect().topを取得 - ファイル:
C:Tempks2techtestst2sticky.html、sticky-ctl.html、sticky-ov.html
| 要素 | スクロール後の top | 判定 |
|---|---|---|
ヘッダー(top: 0) |
0px | 貼り付いた |
サイドバー(align-self: flex-start、top: 20px) |
20px | 貼り付いた |
| サイドバー(stretch のまま) | 1600px | 貼り付かない |
比較用: 同じ構成(sticky-ctl.html、祖先に overflow なし) |
20px | 貼り付いた |
比較用: 同じ構成を overflow: hidden の祖先で囲んだもの(sticky-ov.html) |
-480px | 貼り付かず、スクロールで画面外へ消えた |
stretch のままの場合と、祖先に overflow: hidden がある場合は、スクロール500px時点で貼り付かないことを確認できました。overflow の検証は、同じ構成で囲んだ場合とそうでない場合を別ファイルで比べています。
position: stickyが効かない原因の注意点
topなどを書かないと、sticky はrelativeのように働き、貼り付きません。- 祖先の
overflow: hiddenやoverflow: autoが、貼り付く基準を変えてしまいます。overflow: clipを使うと、回避できる場合があります。 - 親の高さが、貼り付く要素と同じだと、動く範囲がありません。
- 固定ヘッダー下にアンカーリンクで飛ぶと、隠れることがあります(
scroll-margin-topで調整)。
position: stickyが効かない原因でよくあるミス
topの指定忘れ。- Flexbox の子に使っていて、
align-selfを指定していない。 - 祖先(
bodyや ラッパー)にoverflow-x: hiddenを付けている。 - ヘッダーに
z-indexを付け忘れ、下の要素と重なる。
ブラウザ対応・バージョン
position: sticky は、現在の主要ブラウザで使えます。テーブルの thead への指定など、要素による差は MDN の Browser compatibility で確認してください。IE11 は非対応です。
position: stickyが効かない原因のチェックリスト
top(または他の方向)に値を指定したか。- 親に十分な高さがあるか。
- 祖先に
overflowの指定が無いか、DevTools で確認したか。 - Flexbox・Grid の子には
align-self: start(flex-start)を指定したか。
position: stickyが効かない原因のFAQ(よくある質問)
Q. fixed との違いは?
A. fixed は画面に固定され、流れから外れます。sticky は流れの中にあり、親の範囲内でだけ貼り付きます。
Q. 目次を追従させるには?
A. サイドバーを sticky にし、高さが画面より大きい場合は max-height: calc(100vh - 40px); overflow: auto; を併用します。
Q. どの祖先が原因か探すには?
A. DevTools で親要素を順にたどり、overflow の値が visible 以外のものを探します。
筆者の見解(position: stickyが効かない原因)
sticky が効かないときは、CSSの書き間違いより、親や祖先の指定が原因であることが多いと考えます。まず overflow と align-self を疑い、次に top の有無を見る、という順で調べると早く解決できます。JavaScript の scroll イベントで実装していた処理は、まず sticky で置き換えられないか検討するとよいです。
