position: stickyが効かない原因|追従サイドバーの作り方

position: stickyが効かない原因とは

position: sticky は、通常はページの流れの中にいて、スクロールして指定した位置に達したら、画面に貼り付く配置です。追従ヘッダー、目次、サイドバーに使えます。JavaScript を使わずに実装できますが、「書いたのに効かない」ことが多く、原因は決まっています。

基本(構文・仕組み)

MDN によると、sticky の条件は次のとおりです。

  • top bottom left right のいずれかに、auto 以外の値が必要です。
  • 基準は、最も近いスクロール祖先(overflow が hidden scroll auto の要素など)です。
  • 動ける範囲は、親(包含ブロック)の中に限られます。親の端まで来ると、親と一緒にスクロールして消えます。
  • 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 で置き換えられないか検討するとよいです。

position: stickyが効かない原因の関連項目

出典(一次情報)

タイトルとURLをコピーしました