この内容は古いバージョンです。最新バージョンを表示するには、戻るボタンを押してください。
バージョン:4
ページ更新者:爽健
更新日時:2026-06-11 07:07:02

タイトル: スクロールの際のサイドバー固定方法「position: sticky」
SEOタイトル: CSS position:sticky でサイドバー固定(動かない時の罠 / IE 対応 / Tailwind / sticky-top)

この本文には実行可能なタグ等が含まれるため、安全のためHTMLソースとして表示しています。本文データは変更していません。

<table border="1" cellpadding="6" cellspacing="0" style="border-collapse:collapse;background:#fffbea;border:1px solid #f4c430;margin:1em 0;width:100%"><tr><td style="background:#f4c430;color:#000;font-weight:bold;padding:8px">この記事の要点</td></tr><tr><td style="padding:10px"><ul><li><code>position: sticky</code> は <strong>スクロール中は通常配置、閾値を超えると固定</strong>される CSS プロパティ</li><li><strong>必須セット</strong>: <code>position: sticky</code> + <code>top: 0</code>(または bottom/left/right のいずれか)を必ず両方指定</li><li><strong>動かない罠 No.1</strong>: 親要素のいずれかに <code>overflow: hidden / auto / scroll</code> があると無効化される</li><li><strong>動かない罠 No.2</strong>: 親要素に高さがなく、sticky 要素のスクロール余地がない(親 = sticky 要素のサイズ)</li><li>IE 非対応 → <code>position: fixed</code> でフォールバック、または JS で代替(<code>stickybits</code> 等)。Tailwind は <code>sticky top-0</code></li></ul></td></tr></table>
<h2>position: sticky の基本</h2>

<p>CSS の <code>position: sticky</code> は、<strong>スクロール位置が閾値を超えると要素がその位置で固定される</strong>挙動を実現します。サイドバー、ヘッダー、テーブルヘッダー固定によく使われます。</p>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-css">.sidebar {
  position: sticky;  /* これだけではダメ */
  top: 0;            /* 必ず top/bottom/left/right のいずれかが必要 */
}

/* ヘッダー固定 */
header {
  position: sticky;
  top: 0;
  z-index: 100;
  background: white;
}

/* テーブルヘッダー固定 */
thead th {
  position: sticky;
  top: 0;
  background: #f0f0f0;
}</code></pre>
<h2>基本のレイアウト例(サイドバー固定)</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-html"><div class="container">
  <main class="content">
    <!-- 長い本文 -->
  </main>
  <aside class="sidebar">
    <!-- ナビゲーション -->
  </aside>
</div>

<style>
.container {
  display: flex;
  gap: 24px;
  align-items: flex-start;  /* これ重要! */
}

.content {
  flex: 1;
}

.sidebar {
  width: 240px;
  position: sticky;
  top: 16px;       /* 上端から 16px 空ける */
  max-height: calc(100vh - 32px);
  overflow-y: auto;
}
</style></code></pre>
<h2>動かないときのチェックリスト</h2>

<table border="1" cellpadding="6" cellspacing="0" style="border-collapse:collapse;width:100%">
<thead><tr style="background:#fff3cd"><th>チェック項目</th><th>確認方法</th><th>修正</th></tr></thead>
<tbody>
<tr><td><code>top / bottom / left / right</code> 未指定</td><td>DevTools で値確認</td><td><code>top: 0</code> を追加</td></tr>
<tr><td>親に <code>overflow: hidden / auto / scroll</code></td><td>親をたどって overflow を確認</td><td>overflow を <code>visible</code> に</td></tr>
<tr><td>親の高さが sticky 要素と同じ</td><td>親に高さ指定があるか</td><td>親に <code>align-items: flex-start</code> や <code>min-height</code></td></tr>
<tr><td>display: flex の影響</td><td>親が flex で要素が伸びている</td><td><code>align-self: flex-start</code></td></tr>
<tr><td>z-index 不足</td><td>固定後、他要素に隠れる</td><td><code>z-index: 100</code> 追加</td></tr>
<tr><td>テーブル <code>thead</code> で効かない</td><td>古いブラウザ仕様</td><td><code>th</code> 個別に sticky 指定</td></tr>
</tbody>
</table>

<h2>罠1: 親要素の overflow</h2>

<p><strong>祖先要素のどこかに <code>overflow: hidden / auto / scroll</code> があると sticky が無効</strong>になります。スクロールコンテナが別になり、ビューポートに対するスクロールを観測できないためです:</p>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-html"><body>
  <div style="overflow: hidden;">  <!-- ← これが原因 -->
    <div class="sidebar"
         style="position: sticky; top: 0;">
      サイドバー(動かない)
    </div>
  </div>
</body>

<!-- 修正: overflow を消すか、別の方法で hidden 効果を得る --></code></pre>
<h2>罠2: 親要素の高さ</h2>

<p>flex レイアウトの場合、デフォルトで全ての子が同じ高さに伸びます。サイドバー自身の高さ = コンテナの高さ となり、スクロールしてもくっつく余地がない:</p>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-css">/* NG: サイドバーが本文と同じ高さに伸びる */
.container {
  display: flex;
}
.sidebar {
  position: sticky;
  top: 0;
  /* スクロールする余地がない */
}

/* OK: コンテナ側で子の伸びを抑制 */
.container {
  display: flex;
  align-items: flex-start;  /* これ */
}

/* または個別に */
.sidebar {
  align-self: flex-start;
  position: sticky;
  top: 0;
}</code></pre>
<h2>テーブルヘッダーの固定</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-html"><style>
.table-wrap {
  /* overflow を sticky の祖先に置かない! */
  height: 400px;
  overflow-y: auto;  /* これがスクロールコンテナ */
}

.table-wrap th {
  position: sticky;
  top: 0;
  background: #f5f5f5;
  z-index: 1;
}

/* 左列も固定したいとき */
.table-wrap td:first-child,
.table-wrap th:first-child {
  position: sticky;
  left: 0;
  background: #fff;
  z-index: 2;
}

/* 左上の交点 */
.table-wrap th:first-child {
  z-index: 3;
}
</style>

<div class="table-wrap">
  <table>
    <thead>
      <tr><th>項目</th><th>値1</th><th>値2</th></tr>
    </thead>
    <tbody><!-- 大量の行 --></tbody>
  </table>
</div></code></pre>
<h2>scroll-margin と組み合わせる</h2>

<p>sticky ヘッダーがあるとアンカーリンクで「ヘッダーの下に隠れる」問題が起きます。<code>scroll-margin-top</code> で解決:</p>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-css">header {
  position: sticky;
  top: 0;
  height: 60px;
}

/* アンカーで飛んだとき、ヘッダー高さぶん余白を取る */
h2[id] {
  scroll-margin-top: 80px;
}

html {
  scroll-behavior: smooth;  /* スクロールも滑らかに */
}</code></pre>
<h2>IE / 古いブラウザ対応</h2>

<p>IE は完全非対応。Edge Legacy も限定的。フォールバックパターン:</p>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-css">.sidebar {
  /* IE 用フォールバック */
  position: fixed;
  top: 0;
  width: 240px;
}

/* sticky 対応ブラウザでは上書き */
@supports (position: sticky) {
  .sidebar {
    position: sticky;
    top: 0;
  }
}</code></pre>
<p>JS でフォールバックするなら <strong><a href="https://github.com/dollarshaveclub/stickybits" target="_blank" rel="noopener">stickybits</a></strong> が定番。</p>

<h2>Tailwind CSS</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-html"><!-- sticky top-0 で1行 -->
<aside class="sticky top-0 self-start w-64 max-h-screen overflow-y-auto">
  サイドバー
</aside>

<!-- 上から 4 (1rem) 開ける -->
<header class="sticky top-0 z-10 bg-white">
  ヘッダー
</header></code></pre>
<h2>レスポンシブ</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-css">/* PC: 固定サイドバー */
.sidebar {
  position: sticky;
  top: 16px;
}

/* モバイル: sticky を解除 */
@media (max-width: 768px) {
  .sidebar {
    position: static;
  }
}</code></pre>
<h2>FAQ</h2>

<p><strong>Q: <code>position: -webkit-sticky</code> は今も必要?</strong><br />
A: 2017 年以降のブラウザでは不要。Safari 13 以降は <code>position: sticky</code> 単独で動きます。</p>

<p><strong>Q: 親に overflow が必要だが sticky も使いたい</strong><br />
A: 構造を変える必要があります。<strong>sticky 要素を overflow コンテナの外に移動</strong>するか、内側でスクロールする設計に変更してください。</p>

<p><strong>Q: 複数の sticky を縦に並べたい</strong><br />
A: それぞれに違う <code>top</code> を指定(1 個目 <code>top: 0</code>、2 個目 <code>top: 60px</code> 等)。</p>