はい、承知いたしました。position: stickyとposition: fixedの違いと使い分けについて、詳細な解説とサンプルコードを含む約5000語の記事を作成します。
position: stickyとfixedの違いは?使い分けをサンプルコードで徹底解説
Webサイトを閲覧していると、スクロールしても画面の上部に固定され続けるヘッダーや、ページの右下に常に表示されている「トップへ戻る」ボタンなど、特定の要素が画面上の定位置に留まるデザインによく出会います。これらの振る舞いは、主にCSSのpositionプロパティによって実現されています。
中でも、position: fixedとposition: stickyは、要素を「固定する」という点で似ていますが、その仕組みと最適な利用シーンは大きく異なります。この二つの違いを正確に理解し、適切に使い分けることは、ユーザーにとって快適で直感的なウェブサイト(UI/UX)を構築する上で非常に重要です。
しかし、多くの初学者、あるいは経験者でさえも、この二つの挙動の違い、特にposition: stickyが意図通りに動かない原因に悩まされることがあります。
この記事では、position: fixedとposition: stickyのそれぞれの特性を基礎から徹底的に掘り下げ、両者の明確な違いを比較します。さらに、具体的なサンプルコードを交えながら、どのような場面でどちらを使うべきか、その「使い分け」の判断基準を詳しく解説していきます。
この記事を読み終える頃には、あなたはfixedとstickyを自在に操り、より洗練されたインタラクティブなWebページを作成できるようになっているでしょう。
1. CSS positionプロパティの基本
まず、fixedとstickyの話に入る前に、CSSのpositionプロパティそのものについて簡単におさらいしましょう。positionプロパティは、要素がドキュメント内でどのように配置されるかを決定するための非常に強力なツールです。
positionには主に以下の5つの値があります。
static: デフォルト値です。要素はHTMLに記述された通りの順序で、通常のドキュメントフローに従って配置されます。top,right,bottom,left,z-indexといったプロパティは効果がありません。relative:staticと同様に、まずは通常のドキュメントフローに従って配置されます。しかし、topやleftなどのオフセットプロパティを指定することで、本来あるべき位置を基準に要素を相対的に移動させることができます。要素を移動させても、元の場所にはスペースが残ります。absolute: 要素を通常のドキュメントフローから完全に取り除き、最も近いpositionがstatic以外に設定された祖先要素(これを「包含ブロック」と呼びます)を基準に配置します。そのような祖先要素がない場合は、<html>要素が基準となります。元の場所のスペースはなくなります。fixed: 今回の主役の一つです。要素を通常のドキュメントフローから完全に取り除き、ビューポート(ブラウザの表示領域)を基準に配置します。スクロールしても位置は変わりません。sticky: もう一つの主役です。通常はrelativeのように振る舞いますが、スクロール位置が特定の閾値(しきいち)に達すると、fixedのようにその場に留まります。
これらのpositionの値(staticを除く)は、top, right, bottom, leftというオフセットプロパティと組み合わせて使うことで、その真価を発揮します。これらのプロパティは、基準となる位置からどれだけ離れた場所に要素を配置するかを指定します。
それでは、本題であるposition: fixedとposition: stickyについて、それぞれを深く掘り下げていきましょう。
2. position: fixed の徹底解説
2.1. position: fixed とは何か?
position: fixedは、その名の通り「固定された」配置方法です。この値を指定された要素は、ビューポート(ブラウザのウィンドウの表示領域)に対して位置が固定されます。
最大の特徴は、ユーザーがページを上下にスクロールしても、その要素は画面上の同じ位置に留まり続けることです。まるで、Webページという紙の上に、透明なフィルムを一枚重ねて、そのフィルム上に要素を描いているようなイメージです。紙(Webページ)を動かしても、フィルム上の要素は動きません。
2.2. position: fixed の仕組み
position: fixedを理解する上で重要なポイントは2つあります。
- 基準点はビューポート:
top: 0; left: 0;と指定すれば、それはビューポートの左上隅を意味します。bottom: 20px; right: 20px;とすれば、ビューポートの右下隅から20px内側の位置になります。親要素がどこにあろうと、関係ありません。 - ドキュメントフローからの離脱:
fixedを指定された要素は、absoluteと同様に、通常のドキュメントフローから完全に切り離されます。これは、その要素が元々占めていたスペースがなくなり、後続の要素がそのスペースを埋めるように前に詰めてくることを意味します。この挙動は、レイアウト崩れの原因になることがあり、注意が必要です。
2.3. position: fixed の代表的なユースケース
fixedは、サイト全体を通してユーザーに常にアクセスしてほしい機能や情報を提供する場合に非常に有効です。
- 固定ヘッダー/フッター: サイトのロゴやグローバルナビゲーション、重要な告知などを常に表示させておきたい場合。
- 「トップへ戻る」ボタン: 長いページで、ユーザーがいつでもページ最上部に戻れるようにする。
- モーダルウィンドウ: フォーム入力や確認メッセージなど、ユーザーに特定のアクションを促すために、ページの他の部分を操作できなくして表示するウィンドウ。
- チャットウィジェット/クッキー同意バナー: 画面の隅に常に表示され、ユーザーのアクションを待つUI。
- 固定サイドバー: 常に表示させておきたいナビゲーションメニューや広告など。
2.4. サンプルコードと詳細解説
サンプル1: 固定ヘッダー
Webサイトで最もよく見かけるfixedの使い方が、この固定ヘッダーです。
HTML:
“`html
My Website
メインコンテンツ
このセクションはメインのコンテンツです。position: fixed を使ったヘッダーは、スクロールしても常に画面上部に表示され続けます。
しかし、見ての通り、ヘッダーがこのコンテンツの冒頭部分に重なってしまっています。これは、ヘッダーが通常のドキュメントフローから切り離されたために起こる現象です。
Lorem ipsum dolor sit amet, consectetur adipiscing elit. … (大量のテキスト) …
Lorem ipsum dolor sit amet, consectetur adipiscing elit. … (大量のテキスト) …
Lorem ipsum dolor sit amet, consectetur adipiscing elit. … (大量のテキスト) …
Lorem ipsum dolor sit amet, consectetur adipiscing elit. … (大量のテキスト) …
Lorem ipsum dolor sit amet, consectetur adipiscing elit. … (大量のテキスト) …
Lorem ipsum dolor sit amet, consectetur adipiscing elit. … (大量のテキスト) …
“`
CSS (style.css):
“`css
/ 基本的なリセットとスタイル /
body {
margin: 0;
font-family: sans-serif;
line-height: 1.6;
/ ヘッダーの高さ分だけ、コンテンツの開始位置を下げる /
padding-top: 70px;
}
.fixed-header {
/ ここがポイント /
position: fixed;
top: 0; / ビューポートの上端に配置 /
left: 0; / ビューポートの左端に配置 /
width: 100%; / 幅をビューポートいっぱいに広げる /
height: 70px; / ヘッダーの高さを指定 /
background-color: #333;
color: white;
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 20px;
box-sizing: border-box;
/ 他の要素より手前に表示するためのz-index /
z-index: 1000;
}
.logo {
margin: 0;
font-size: 1.5em;
}
nav ul {
margin: 0;
padding: 0;
list-style: none;
display: flex;
}
nav li {
margin-left: 20px;
}
nav a {
color: white;
text-decoration: none;
}
.main-content {
padding: 20px;
}
“`
コード解説:
position: fixed;:.fixed-headerをビューポート基準で固定します。top: 0; left: 0;: ヘッダーをビューポートの左上隅にぴったりと配置します。width: 100%;: 幅をビューポートの100%に設定し、画面全体に広がるヘッダーにします。z-index: 1000;:z-indexは要素の重なり順(奥行き)を指定します。値が大きいほど手前に表示されます。fixed要素は他のコンテンツの上に重なることが多いため、意図せず他の要素の下に隠れてしまわないように、十分に大きな値を設定しておくのが一般的です。body { padding-top: 70px; }: これが非常に重要な点です。前述の通り、fixed要素はドキュメントフローから離脱するため、何もしないと後続の要素(この場合は.main-content)がヘッダーの下に潜り込んでしまいます。これを避けるため、body(またはメインコンテンツのコンテナ)に、ヘッダーの高さと同じだけのpadding-top(またはmargin-top)を設定し、コンテンツの開始位置を強制的に押し下げています。
2.5. position: fixed の注意点
- コンテンツの重なり: 上記のサンプルで解説した通り、必ず後続コンテンツの開始位置を調整する必要があります。
- レスポンシブデザイン: スマートフォンのように画面幅が狭いデバイスでは、固定ヘッダーやフッターが画面の貴重な表示領域を占有しすぎて、コンテンツが読みにくくなることがあります。メディアクエリを使って、特定の画面サイズ以下では固定を解除する(
position: static;に戻すなど)といった配慮が必要です。 - 親要素の
transform等による影響: 少し高度な話ですが、親要素にtransform,filter,perspectiveのいずれかのプロパティがnone以外で指定されていると、fixed要素の基準点がビューポートではなく、その親要素になってしまいます。これは意図しない挙動の原因となることがあるため、覚えておくとデバッグの際に役立ちます。
3. position: sticky の徹底解説
3.1. position: sticky とは何か?
position: stickyは、relativeとfixedの「いいとこ取り」をしたような、非常に便利な配置方法です。
stickyを指定された要素は、最初はposition: relativeのように、通常のドキュメントフローに従って配置されています。しかし、ユーザーがページをスクロールし、その要素がtop, right, bottom, leftで指定された閾値(オフセット)に達すると、position: fixedのようにその場に「貼り付き(stick)」ます。
そして、その要素が含まれている親コンテナの範囲をスクロールし終えると、また元のドキュ-メントフローに戻り、ページと一緒にスクロールしていきます。
この挙動を例えるなら、付箋(ポストイット)のようなものです。最初はノートの特定の位置に貼られていますが(relativeの状態)、ノートをめくっていくと、その付箋が指に引っかかって、指の位置で固定されます(fixedの状態)。そして、そのページをめくり終えると、付箋は指から離れてまたノートと一緒に動いていきます。
3.2. position: sticky の仕組み
stickyを正しく機能させるには、いくつかの条件を理解する必要があります。
- オフセットの指定が必須:
top,right,bottom,leftのいずれか最低一つを指定しなければなりません。これが、どの位置で「貼り付く」かを決めるトリガーになります。例えばtop: 0;と指定すれば、要素の上端がビューポートの上端に達した瞬間に貼り付きます。 - ドキュメントフローには留まる:
fixedと違い、sticky要素はドキュメントフローから離脱しません。そのため、sticky要素が元々あった場所にはスペースが確保されたままになり、レイアウトが崩れにくいという大きなメリットがあります。 - 親コンテナの範囲内でのみ有効:
sticky要素の動作は、最も近いスクロール可能な祖先要素の範囲に限定されます。要素が貼り付いていられるのは、その親コンテナの表示領域内だけです。親コンテナが画面から見えなくなれば、sticky要素も一緒にスクロールアウトします。
3.3. position: sticky の代表的なユースケース
stickyは、特定の文脈(コンテキスト)の中で情報を固定表示したい場合に絶大な効果を発揮します。
- セクションごとの見出し: アルファベット順のリストや、複数の章からなる長い記事などで、現在表示されているセクションの見出しを画面上部に固定する。
- 表(テーブル)のヘッダー: 縦に長い表をスクロールしても、列の見出し(
<th>)を常に表示させておくことで、データの可読性を高める。 - スクロール追従サイドバー: 記事本文を読み進めている間、関連情報や目次をサイドバーに固定表示する。ただし、記事の末尾(フッターなど)に達したら、サイドバーも一緒にスクロールアウトさせたい場合。
- 入力フォームの操作ボタン: 縦に長い入力フォームで、送信ボタンや保存ボタンを常に画面下部や上部に表示させておく。
3.4. サンプルコードと詳細解説
サンプル2: セクション見出しの固定
複数のセクションを持つページで、それぞれの見出しがスクロールに応じて画面上部に貼り付く例です。
HTML:
“`html
セクション A
この見出し「セクション A」は、position: sticky に設定されています。
スクロールして、この見出しが画面の上端に達すると、そこに貼り付きます。
そして、次の見出し「セクション B」が迫ってくると、押し出されるようにしてスクロールしていきます。
Lorem ipsum dolor sit amet, consectetur adipiscing elit. … (大量のテキスト) …
Lorem ipsum dolor sit amet, consectetur adipiscing elit. … (大量のテキスト) …
セクション B
「セクション B」の見出しが画面上部に到達しました。今度はこの見出しが固定されます。
Lorem ipsum dolor sit amet, consectetur adipiscing elit. … (大量のテキスト) …
Lorem ipsum dolor sit amet, consectetur adipiscing elit. … (大量のテキスト) …
Lorem ipsum dolor sit amet, consectetur adipiscing elit. … (大量のテキスト) …
セクション C
最後に「セクション C」の見出しが固定されます。
sticky要素の振る舞いは、親要素の範囲に限定されることを覚えておくのが重要です。
Lorem ipsum dolor sit amet, consectetur adipiscing elit. … (大量のテキスト) …
Lorem ipsum dolor sit amet, consectetur adipiscing elit. … (大量のテキスト) …
Lorem ipsum dolor sit amet, consectetur adipiscing elit. … (大量のテキスト) …
“`
CSS (style.css):
“`css
body {
margin: 0;
font-family: sans-serif;
line-height: 1.6;
}
.container {
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
.sticky-header {
/ ここがポイント /
position: sticky;
top: 0; / 画面上端から0pxの位置で貼り付く /
background-color: #f0f0f0;
padding: 10px;
margin: 20px -10px; / 見出しの左右paddingを相殺 /
/ 後続のsticky-headerが来た時に、このヘッダーが下にならないように /
z-index: 100;
}
“`
コード解説:
position: sticky;:.sticky-header要素にスティッキーな振る舞いを適用します。top: 0;: スクロールによって、このh2要素の上辺がビューポートの上辺に到達した瞬間に、その位置に固定されるように指定します。top: 20px;とすれば、上から20pxの余白を空けて固定されます。background-colorの指定:sticky要素が貼り付くとき、それは他のコンテンツの上に重なります。背景色を指定しておかないと、下のコンテンツが透けて見えてしまい、文字が読みにくくなります。z-index: 100;: 複数のsticky要素がある場合、後から来たものが上になるように、z-indexを指定しておくと意図した通りの重なり順になります。
このサンプルをブラウザで表示してスクロールすると、「セクション A」の見出しが画面上部に達すると固定され、そのままスクロールを続けると「セクション B」の見出しが「セクション A」を押し上げるようにして入れ替わり、今度は「セクション B」が固定される、というダイナミックな動きが確認できます。これはCSSだけで実現できています。
3.5. position: sticky の最大の注意点: 親要素のoverflow
position: stickyが「効かない!」という問題に直面したとき、その原因の9割はこれです。
stickyは、祖先要素にoverflow: hidden;, overflow: scroll;, overflow: auto;のいずれかが指定されていると、その祖先要素の範囲内でしか機能しません。
言い換えると、sticky要素は、それを内包している「スクロール可能なコンテナ」の境界を越えて貼り付き続けることはできないのです。
多くのWebサイトでは、全体のレイアウトを管理するために、ラッパー要素(例: <div id="wrapper">)にoverflow: hidden;を設定していることがあります。このような構造の中でstickyを使おうとすると、stickyの親が#wrapperになってしまい、#wrapperがスクロールするわけではない(スクロールするのはbodyやwindow)ため、stickyは全く機能しないように見えます。
解決策: position: stickyを使いたい要素から、そのstickyを機能させたいスクロールコンテナ(通常はbody)までの全ての祖先要素に、overflowの指定がないか確認してください。もしoverflow: hiddenなどが見つかったら、それを削除するか、レイアウト構造を見直す必要があります。これはstickyを使いこなす上で最も重要な知識です。
4. position: sticky と position: fixed の比較
ここまでの解説で、両者の違いがかなり明確になってきたかと思います。情報を整理するために、比較表と要点でまとめてみましょう。
4.1. 比較表
| 項目 | position: fixed |
position: sticky |
|---|---|---|
| 基準点 | ビューポート (ブラウザの表示領域) | 最も近いスクロール可能な祖先要素 |
| ドキュメントフロー | 離脱する (元のスペースはなくなる) | 離脱しない (元のスペースは保持される) |
| 挙動 | 常に固定 (スクロールに影響されない) | 条件付きで固定 (スクロール位置に応じて変化) |
| オフセット指定 | 必須 (位置を決めるため) | 必須 (貼り付く位置を決めるため) |
| 主な用途 | サイト全体のグローバルなUI | 特定セクション内のコンテキストに応じたUI |
| 注意点 | コンテンツの重なり対策が必須 | 親要素のoverflowプロパティに注意 |
4.2. 核心的な違いのまとめ
- 基準点の違い(これが全て!):
fixedは常に「画面」を基準にします。stickyは「親のスクロールエリア」を基準にします。この違いが、すべての挙動の違いの根源です。 - レイアウトへの影響:
fixedはドキュメントフローから消えてしまうため、後続の要素が詰まり、レイアウト調整(paddingやmarginの追加)が必要です。一方、stickyは元の位置にスペースを残したままなので、レイアウトが崩れにくく、より安全に導入できます。 - 動的な性質:
fixedは静的で、一度設定したら常に同じ振る舞いをします。stickyは動的で、スクロールというユーザーのアクションに応じて、relativeとfixedの間で状態が変化します。
5. 使い分けの判断基準
それでは、具体的にどのような状況でどちらを選べば良いのでしょうか。明確な判断基準を以下に示します。
5.1. position: fixed を選ぶべきとき
問い:「この要素は、ユーザーがサイトのどのページ、どの部分を見ていても、常に画面の同じ場所に表示されていてほしいか?」
この問いに「はい」と答えるなら、position: fixedが最適です。
- グローバルナビゲーション: サイト全体の道しるべ。
- トップへ戻るボタン: ページのどこからでも最上部へ。
- クッキー同意バナー: ユーザーが同意するまで表示。
- モーダルウィンドウ/オーバーレイ: 他の操作を中断させて注目させる。
これらの要素は、ページの特定のコンテンツとは独立して、常にユーザーに提供されるべき機能です。だからこそ、コンテンツの流れとは無関係な「ビューポート」を基準とするfixedが適しています。
5.2. position: sticky を選ぶべきとき
問い:「この要素は、ある特定のセクションをユーザーが見ている間だけ、画面の定位置にいてほしいか?」
この問いに「はい」と答えるなら、position: stickyが最適です。
- 記事の見出しリスト(サイドバー): 記事本文を読んでいる間は追従してほしいが、記事を読み終えてフッター部分に来たら、一緒にスクロールアウトしてほしい。
- テーブルのヘッダー: 長いデータテーブルを見ている間は列名が固定されていてほしいが、テーブル全体が画面から消えたら、ヘッダーも当然消えてほしい。
- Eコマースサイトのフィルターオプション: 商品リストをスクロールしている間は、フィルターを常に使えるようにしておきたい。
これらの要素は、特定のコンテンツ(記事、テーブル、商品リスト)と密接に関連しています。そのコンテンツの文脈の中でのみ固定されるべきなので、「親コンテナ」を基準とするstickyがまさにうってつけなのです。
6. 発展的なトピックとベストプラクティス
6.1. sticky要素の状態をJavaScriptで検知する
sticky要素が実際に貼り付いている(fixedの状態になっている)かどうかをCSSだけで判断することはできません。しかし、JavaScriptのIntersection Observer APIを使えば、これを検知してスタイルを変更するといった高度な演出が可能です。
例えば、stickyヘッダーが貼り付いた瞬間に、ヘッダーに影をつけたり、背景色を少し変えたりすることで、ユーザーに状態の変化を視覚的に伝えることができます。
簡単な実装の考え方は以下の通りです。
sticky要素の直前(または直後)に、高さ1pxの「監視用要素(sentinel)」を配置します。Intersection Observerで、この監視用要素がビューポートに出入りするのを監視します。sticky要素がtop: 0で貼り付く設定の場合、監視用要素がビューポートから見えなくなったとき(画面上部を通過したとき)が、sticky要素が貼り付いた瞬間です。- このタイミングで、JavaScriptを使って
sticky要素に特定のクラス(例:.is-stuck)を付与し、CSSでそのクラスのスタイルを定義しておきます。
これは少し複雑ですが、stickyの表現力をさらに高めるテクニックとして覚えておくと良いでしょう。
6.2. パフォーマンスについて
スクロールに応じて要素を固定する振る舞いは、かつてはJavaScriptのscrollイベントを使って実装されていました。しかし、scrollイベントは非常に頻繁に発火するため、処理が重くなりがちで、スクロールがカクカクする原因になることがありました。
position: stickyとposition: fixedは、ブラウザがネイティブに最適化された方法で処理するため、JavaScriptを使うよりもはるかにパフォーマンスが良いです。CSSで実現できることは、できるだけCSSで実装するのが現代のWeb開発のセオリーです。
7. まとめ
position: fixedとposition: stickyは、どちらも要素を画面に固定するための強力なツールですが、その根本的な哲学が異なります。
-
position: fixedは 「グローバル」 です。ビューポートという絶対的な基準を持ち、ドキュメントの構造から独立して、常にそこに存在し続けます。サイト全体の案内役や、常に必要な機能のために使います。 -
position: stickyは 「ローカル」 です。親コンテナという相対的な基準を持ち、特定の文脈の中でのみその能力を発揮します。長いコンテンツを読みやすくするための補助役として、その真価を発揮します。
この二つの違いを理解し、「このUIはサイト全体のものか、それとも特定のコンテンツエリアのものか?」を自問することで、あなたは常に正しい選択ができるようになります。
fixedによるレイアウト調整の必要性や、stickyがoverflowに影響されるという「癖」をしっかりと把握しておけば、デバッグに時間を費やすこともなくなるでしょう。
CSSのpositionプロパティは、Webデザインの表現力を大きく広げてくれます。今回学んだ知識を活かして、ユーザーにとってより快適で、より直感的なウェブサイトを構築していきましょう。