あなたに合った学習プランは?LINE適正コース診断はこちら プログラミングが全て無料で学習可能!
【CSS】position: stickyが効かない原因7選!overflowや高さを解説 - 忍者CODEマガジン

【CSS】position: stickyが効かない原因7選!overflowや高さを解説

プログラミング言語の辞書

CSSで見出しやサイドバーを追従させたいときに便利なのがposition: sticky;です。しかし、実際に書いてみると「stickyが効かない」「スクロールしても止まらない」「途中で消える」と悩むことがあります。position: stickyが効かない原因の多くは、topなどの位置指定漏れ、親要素のoverflow、親要素の高さ不足、またはstickyの仕様への誤解です。

この記事では、stickyの基本、効かない原因、実務でのチェックポイント、fixedとの違いまで解説します。

position: stickyとは?

position: sticky;とは、通常の位置に表示されていた要素が、スクロールによって指定位置に達したときだけ固定されるCSSです。

.side-heading {
  position: sticky;
  top: 16px;
}

この例では、.side-headingが画面上から16pxの位置に来るまでは通常通りスクロールされ、そこに達すると追従するように表示されます。

stickyは「relative」と「fixed」の中間のような動き

stickyは、最初から画面に固定されるわけではありません。通常の文書の流れに残ったまま表示され、スクロール位置によって固定状態に切り替わります。

指定 特徴 使う場面
relative 元の位置を基準に少しずらす 位置調整、absoluteの基準点
fixed 画面に完全固定する 固定ヘッダー、追従ボタン
sticky 指定位置に来たら粘着する 追従見出し、サイドバー、表の見出し

stickyは「最初は通常配置、指定位置に来たら固定」と覚えると分かりやすいです。

【結論】stickyが効かないときに最初に見るポイント

stickyが効かないときは、まず次の順番で確認しましょう。

  • topbottomなどの位置指定があるか
  • 親や祖先要素にoverflow: hidden;などがないか
  • sticky要素の親に十分な高さがあるか
  • sticky要素が親要素の範囲を超えて固定されると思い込んでいないか
  • テーブルやflex/grid内で使っていて、親の構造が複雑になっていないか

stickyは画面全体に無制限で固定される指定ではありません。基本的には、親要素やスクロールコンテナの範囲内で動くと考えましょう。

原因1:topなどの位置指定を書いていない

stickyで最も多いミスが、position: sticky;だけを書いて、topなどの位置指定を書いていないケースです。

【HTML構造】
<aside class="sidebar">
  <h2 class="sticky-title">目次</h2>
  <ul>
    <li>見出し1</li>
    <li>見出し2</li>
  </ul>
</aside>
【NG例】
.sticky-title {
  position: sticky;
}

これだけでは、どの位置で粘着すればよいかブラウザが判断できません。

【OK例】
.sticky-title {
  position: sticky;
  top: 0;
}

top: 0;は「画面上部に到達したら固定する」という意味です。stickyを使うときは、topbottomなど、どこで固定するかを必ず指定しましょう。

原因2:親要素にoverflowが指定されている

stickyが効かない原因として非常に多いのが、親や祖先要素にoverflowが指定されているケースです。

【HTML構造】
<div class="layout">
  <main class="article">長い本文</main>
  <aside class="sidebar">追従メニュー</aside>
</div>
【NG例】
.layout {
  overflow: hidden;
  display: grid;
  grid-template-columns: 1fr 280px;
  gap: 32px;
}

.sidebar {
  position: sticky;
  top: 24px;
}

overflow: hidden;overflow: auto;overflow: scroll;などが指定されると、その要素がスクロールの基準になり、期待した画面基準のstickyにならないことがあります。

【OK例】
.layout {
  display: grid;
  grid-template-columns: 1fr 280px;
  gap: 32px;
}

.sidebar {
  position: sticky;
  top: 24px;
  align-self: start;
}

対策:親や祖先要素のoverflowを確認する

stickyが効かないときは、対象要素だけでなく、親要素・祖先要素のCSSも確認しましょう。

【確認するCSS】
overflow: hidden;
overflow: auto;
overflow: scroll;
overflow: overlay;

不要なoverflowであれば削除します。どうしても必要な場合は、stickyを使う場所を見直したり、sticky要素をそのコンテナの外側に移動したりします。見た目のはみ出しを隠すために親へoverflow: hidden;を入れていると、stickyが効かない原因になることがあります。

原因3:親要素の高さが足りない

stickyは親要素の範囲内で粘着します。そのため、親要素の高さがsticky要素とほとんど同じだと、固定される余地がありません。

【効きにくい例】
<div class="sidebar-wrap">
  <aside class="sidebar">追従メニュー</aside>
</div>
【NG例】
.sidebar-wrap {
  /* 中身と同じ高さしかない */
}

.sidebar {
  position: sticky;
  top: 20px;
}

この場合、.sidebar-wrapの高さが中身と同じ程度しかないと、stickyが動くための余白がありません。

対策:stickyの親に十分な高さを作る

記事本文とサイドバーを横並びにする場合は、sticky要素の親が本文と同じ高さを持つような構造にします。

<div class="page-layout">
  <main class="article">長い本文</main>
  <aside class="sidebar">追従メニュー</aside>
</div>
.page-layout {
  display: grid;
  grid-template-columns: 1fr 280px;
  gap: 32px;
}

.sidebar {
  position: sticky;
  top: 24px;
  align-self: start;
}

stickyは「動ける範囲」が必要です。親要素が短すぎると、固定されたように見えません。

原因4:親要素の範囲を超えて固定されると思っている

stickyは、親要素やスクロール範囲の中で粘着します。親要素の範囲が終わると、sticky要素もそこで止まります。「途中まで追従していたのに消えた」「最後まで固定されない」という場合、親要素の終端に達している可能性があります。

【NG例】
<section class="short-section">
  <aside class="sidebar">追従メニュー</aside>
</section>
.short-section {
  height: 300px;
}

.sidebar {
  position: sticky;
  top: 24px;
}
【OK例】
<div class="page-layout">
  <main class="article">長い本文</main>
  <aside class="sidebar">追従メニュー</aside>
</div>
.page-layout {
  display: grid;
  grid-template-columns: 1fr 280px;
  gap: 32px;
}

.sidebar {
  position: sticky;
  top: 24px;
  align-self: start;
}

対策:どの範囲で追従させたいかを決める

ページ全体で常に固定したいならfixed、特定のセクション内だけ追従したいならstickyを使います。

やりたいこと おすすめ 理由
画面上に常に表示したい position: fixed; 画面を基準に固定できる
セクション内だけ追従させたい position: sticky; 親要素の範囲内で止まる

原因5:flexやgridの子要素で伸びている

flexやgridの中でstickyを使うと、子要素が親の高さに合わせて伸びてしまい、stickyが効いていないように見えることがあります。

【NG例】
.page-layout {
  display: grid;
  grid-template-columns: 1fr 280px;
  gap: 32px;
}

.sidebar {
  position: sticky;
  top: 24px;
}

この場合は、sticky要素にalign-self: start;を指定すると改善することがあります。

【OK例】
.sidebar {
  position: sticky;
  top: 24px;
  align-self: start;
}

横並びレイアウト内でstickyを使う場合は、sticky要素が縦に引き伸ばされていないか確認しましょう。

原因6:テーブルの見出し固定で使い方を間違えている

表の見出し行を固定したい場合、theadthにstickyを使うことがあります。

【NG例】
th {
  position: sticky;
  top: 0;
}

背景色や重なり順がないと、スクロール時に本文セルと重なって読みにくくなることがあります。

【OK例】
th {
  position: sticky;
  top: 0;
  background: #fff;
  z-index: 1;
}

テーブルでは、背景色やz-indexを指定しておかないと、スクロール時に本文セルと重なって見づらくなることがあります。

原因7:z-indexや背景色の指定が足りない

sticky自体は効いていても、他の要素の下に隠れて「効いていない」ように見えることがあります。

【NG例】
.sticky-title {
  position: sticky;
  top: 0;
}

この状態では、スクロール中に後続要素が重なり、sticky要素が隠れているように見えることがあります。

【OK例】
.sticky-title {
  position: sticky;
  top: 0;
  z-index: 10;
  background: #fff;
}

固定される要素には、必要に応じてz-indexと背景色を指定しましょう。

sticky要素は、重なり順と背景色までセットで考えると表示崩れを防ぎやすくなります。

実務トラブル1:z-indexを指定しても下に潜るときの罠

z-indexを指定しているのにsticky要素が他の要素の下に潜る場合、スタッキングコンテキストが原因になっていることがあります。

スタッキングコンテキストとは、要素の重なり順を管理する独立したグループのようなものです。親要素が別の重なりグループを作っていると、子要素に大きなz-indexを指定しても、その親の外側にある要素より前に出られない場合があります。

【NG例】
<div class="main-area">
  <h2 class="sticky-title">追従見出し</h2>
</div>

<header class="site-header">固定ヘッダー</header>
.main-area {
  position: relative;
  z-index: 1;
}

.sticky-title {
  position: sticky;
  top: 0;
  z-index: 9999;
}

.site-header {
  position: fixed;
  top: 0;
  z-index: 10;
}

この例では、.sticky-titlez-indexが大きくても、親の.main-areaz-index: 1;の階層にいるため、別階層の固定ヘッダーより前面に出ないことがあります。

【OK例】
.main-area {
  position: relative;
  z-index: auto;
}

.sticky-title {
  position: sticky;
  top: 0;
  z-index: 20;
  background: #fff;
}

.site-header {
  position: fixed;
  top: 0;
  z-index: 100;
}

実務では、ヘッダー、sticky見出し、モーダルなどの重なり順を役割ごとに整理しておくと安全です。z-indexを大きくしても効かない場合は、対象要素ではなく親や祖先要素のz-index、transform、opacityなどを確認しましょう。

実務トラブル2:Safariでposition: stickyが動かないケース

現在の主要ブラウザではposition: sticky;は広く利用できますが、古いSafariや古いiOS Safariまで対応する案件では、-webkit-stickyを併記していたコードを見かけることがあります。

【古い環境まで見る場合の例】
.sticky-title {
  position: -webkit-sticky;
  position: sticky;
  top: 0;
}

ただし、現在の新規制作で必ず必要という意味ではありません。対象ブラウザに古いSafariが含まれる場合だけ、実機や検証環境で確認しましょう。

Safariでは親要素のoverflowや高さも特に確認する

Safariでstickyが効かないように見える場合も、原因はベンダープレフィックスだけとは限りません。

  • 親や祖先要素にoverflow: hidden;がないか
  • sticky要素の親に十分な高さがあるか
  • topなどの位置指定があるか
  • flex/grid内で伸びていないか

Safariだけで崩れる場合でも、まずは基本原因を確認しましょう。古いSafari向けの-webkit-stickyは、対応対象が古い端末を含む場合の補助策です。

実務トラブル3:スクロールコンテナがwindowではなくdivになっている

stickyは、画面全体だけでなく、スクロールする親要素の中でも動きます。つまり、スクロールコンテナがwindowではなく特定のdivになっている場合、stickyの基準もそのコンテナ側になります。

【HTML構造】
<div class="scroll-box">
  <h2 class="sticky-title">この枠の中で固定</h2>
  <p>長い本文が入ります。</p>
</div>
【OK例】
.scroll-box {
  height: 320px;
  overflow: auto;
  border: 1px solid #d8e2ea;
}

.sticky-title {
  position: sticky;
  top: 0;
  z-index: 1;
  background: #fff;
}

この場合、.sticky-titleはページ全体ではなく、.scroll-boxの中で固定されます。

ページ全体に対して追従させたい場合

ページ全体のスクロールに対して追従させたい場合は、sticky要素をoverflow: auto;overflow: hidden;を持つ親の外へ出すことを検討します。

【見直し例】
<h2 class="sticky-title">ページ全体で追従</h2>

<div class="scroll-box">
  <p>この中だけスクロールするコンテンツ</p>
</div>

overflow: auto;のdiv内にsticky要素を入れると、そのdivの中でstickyが動きます。ページ全体で追従させたい場合は、HTML構造を見直しましょう。

position: stickyが効かないときのチェックリスト

最後に、実務で確認する順番をまとめます。

確認項目 見るポイント 対策
位置指定 topなどがあるか top: 0;などを指定する
overflow 親や祖先にoverflowがないか 不要なら外す、構造を変える
親の高さ stickyが動ける範囲があるか 親やレイアウト構造を見直す
flex/grid 子要素が伸びていないか align-self: start;を指定する
重なり順 他の要素に隠れていないか z-indexと背景色を指定する
階層 親のz-indexやtransformで別階層になっていないか 親や祖先要素のCSSも確認する
Safari 古いSafariまで対応対象に含むか 必要に応じてposition: -webkit-sticky;を検証する
スクロールコンテナ windowではなくdiv内スクロールになっていないか stickyを置くHTML構造を見直す

公式ドキュメントも確認する

stickyの仕様を正確に確認したい場合は、MDN Web Docsの公式情報も参考になります。

まとめ:stickyが効かないときは親要素とtopを確認しよう

この記事では、CSSのposition: sticky;が効かない原因を解説しました。stickyが効かないときは、まずtopなどの位置指定、親要素のoverflow、親要素の高さを確認しましょう。

stickyは便利ですが、常に画面に固定するfixedとは違い、親要素やスクロール範囲の影響を受けます。さらに、z-indexの階層、古いSafari、div内スクロールなども絡むと、効いていないように見えることがあります。ページ全体で固定したいのか、特定の範囲内で追従させたいのかを決めてから使うと、トラブルを防ぎやすくなります。