CSSのposition: stickyが効かない|まず確認したいこと

こんにちは、さるまりんです。

今日はCSSの

position: sticky;

についてです。

スクロールしていって、ある場所まで来たら、その位置についてくる。

そんな表示を作ることができます。

例えば、

.header {
    position: sticky;
    top: 0;
}

と書けば、スクロールしたときに上部へくっついてくれそうです。

ところが、

あれ?動かない。

そうです、うまく効いてくれないことがあるんです。

今回は、position: sticky が思ったように動かないときに、まず確認したいところを見てみます。

まずは動く形を見てみます

例えば、こんなHTMLがあるとします。

<div class="container">
    <div class="sticky-header">
        ここがついてきます
    </div>

    <div class="content">
        <p>ここに長い内容があります。</p>
        <p>スクロールできるくらい内容を入れます。</p>
    </div>
</div>

CSSは、

.sticky-header {
    position: sticky;
    top: 0;
    background: white;
}

/* 動きを試すために、内容の部分を長くしています */
.content {
    min-height: 150vh;
}

です。

150vh は、画面の高さの約1.5倍です。短い文章でもスクロールして試せるように入れています。

この例では、親やその外側の要素に overflow: hidden などが指定されていないものとします。

スクロールして、sticky-header が画面の上まで来ると、

top: 0;

の位置で止まり、そのままついてくるようになります。ただし、ついてくるのは .container の下端に届くまでです。

ずっと同じ位置に固定する position: fixed とは少し違うんですね。

以前、スクロールしてもついてくるボタンを作ったときには、

position: fixed;

を使いました。

スクロールしてもついてくるボタンを作る(HTML / CSS / JavaScript)

sticky は、最初から画面に固定する指定ではなく、

ある位置までスクロールしてきたら、そこからついてくる

という動きをします。

topを書いている?

まず確認したいのが、

top: 0;

です。

例えば、

.sticky-header {
    position: sticky;
}

だけでは、どの位置でくっつくのかが指定されていません。

そこで、

.sticky-header {
    position: sticky;
    top: 0;
}

とします。

top: 0 は、

上から0の位置でくっつく

ということですね。

少し間を空けたいなら、

top: 20px;

のようにもできます。

ページの上に固定されたメニューがある場合も、その高さに合わせて間を空けると重なりを避けられます。

今回は上側にくっつけたいので top を使っています。上下方向のstickyでは、top と bottom の両方が未指定のままだと、上下にはくっつきません。

position: sticky を書いたのに動かないときは、まず top などの指定があるかを確認してみるとよさそうです。

親要素のoverflowを確認する

次に確認したいのが、

overflow

です。

これは、内容が枠からはみ出したときに、どう扱うかを決める指定です。

例えば、親要素や、そのさらに外側の要素に、

.wrapper {
    overflow: hidden;
}

や、

.wrapper {
    overflow: auto;
}

が指定されていることがあります。

sticky は、必ず画面を基準にしてくっつくわけではありません。

親から外側へたどったとき、overflow: hidden、auto、scroll などでスクロールの仕組みを持つ、一番近い要素の枠が基準になります。

ここで少しややこしいのが、その枠にスクロールバーが出ていなくても、基準になることです。

overflow: hidden でも、この影響があります。

例えば、外側の枠に overflow: auto があっても、その枠の中身が短ければ、枠の中ではスクロールしません。

その状態でページ全体をスクロールしても、思っていたように画面の上へくっつかないことがあります。

つまり、

「画面に対してstickyになると思っていたのに、ならない」

ということですね。

自分で overflow を書いていなくても、使っているCSSやテーマなどで設定されていることもあります。

そんなときは、ブラウザーの開発者ツールで、親だけでなく、その外側の要素も順番にたどってみます。

overflow、overflow-x、overflow-y がどうなっているかを確認すると、原因が見つかるかもしれません。

なお、overflow-x: hidden だけでも、組み合わせによっては縦方向の overflow-y が auto として扱われます。横にはみ出すのを隠すための指定も、確認してみるとよさそうです。

原因を探すときは、開発者ツールで一時的に指定を外して、動きが変わるか試してみます。必要な表示まで変わってしまうことがあるので、そのまま全部消してしまう前に、何のための指定かも確認したいですね。

stickyできる範囲はある?

position: sticky は、どこまでもついてくるわけではありません。

基本的には、その要素が入っている範囲の中で動きます。

例えば、

<div class="container">
    <div class="sticky-header">
        ここがついてきます
    </div>
</div>

のように、親の中身がstickyにした要素だけだと、親と子が同じくらいの高さになることがあります。

これでは、親の中に動ける余地がありません。

ページのほかの部分が長くても、この親の範囲を越えてついてくるわけではないんですね。

最初の例では、長い内容も同じ .container の中に入れました。そうすることで、その内容を読んでいる間、見出しがついてくる形になります。

親の高さを無理に大きくする前に、ついてきてほしい内容とstickyにした要素が、同じ枠の中にあるかを確認してみるとよさそうです。

flexやgridで、縦に伸びていない?

横並びなどを作る flex や grid を使っていると、子要素の高さがそろうように、縦に伸びることがあります。

その結果、stickyにした要素が親と同じくらいの高さになると、ここでも動ける余地がなくなってしまいます。

例えば、横並びのflexなら、sticky側に、

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

と書いて、縦に引き伸ばされず、上にそろうようにできます。

gridの場合は、

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

のようにします。

flexは並べる向きによって効く方向が変わるので、ここでは横並びの場合の例です。

まずは、stickyにした要素が縦いっぱいに伸びていないかを見るくらいで大丈夫です。

まずは、このあたりから

position: sticky が効かないときは、まず次のところを確認してみます。

  • top など、くっつく位置を書いているか
  • 親や、その外側の要素に overflow の指定がないか
  • 親の中に、stickyが動ける余地があるか
  • flexやgridで、stickyにした要素が縦いっぱいに伸びていないか

見た目だけだと、どこが原因なのか分かりにくいこともあります。

そんなときは、開発者ツールで一つずつ指定を外したり、親の高さを見たりしながら確認していくと、少しずつ見えてきそうです。

僕自身、CSSはまだまだ理解したいことがたくさんあります。

こうした「どうして動かないんだろう?」を一つずつ試しながら、使えるものを増やしていきたいですね。

読んでくださってありがとうございます。

それではまた!