【CSS】position:sticky;で要素を追従させる方法

【CSS】position:sticky;で要素を追従させる方法

position:sticky;って?

ヘッダーナビゲーションやサイドメニューなど、その位置までスクロールしたらそこから画面に固定して追従させておきたい時があります。 特にヘッダーをこの手法で固定する場合は「スティッキーヘッダー」と言われているようです。 
このような実装は、JSでスクロール位置を計算しposition:fixed;のCSSを書いたclassを任意のタイミングで付け外しする、といった対応で実現できますが、CSSのみで実現できるのがposition:sticky;です。

文字だけではわかりにくいのでとりあえずデモを作ってみました。

position:sticky;でスティッキーヘッダーを実装したデモ

今回は簡単なコード例を元に使い方と注意点をメモします。

position:sticky;の使い方

まずはhtml。追従させたい要素とその親要素、兄弟要素を用意します。 
なぜ兄弟要素が必要かは後述します。

<div class="sticky-container">
  <div class="sticky">追従させたい要素</div>
  <div class="sticky-next">兄弟要素</div>
  <!-- /.sticky-next -->
</div>
<!-- /.sticky-container -->

CSSの指定はこちら。

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

CSSはたったこれだけです。
これで.stickyクラスの要素が.sticky-containerクラスの要素内でスクロールに追従するようになります。追従させる位置を変えたい時は、topやleftなどで調整できます。

これだけなので指定はめちゃ簡単なのですが、普通に使おうとすると動く時と動かない時があります。 けっこうクセの強いプロパティなので、CSSを指定するだけでなくいくつかの条件をクリアしていないと動きません。

親要素と兄弟要素(高さ)が必要

position:sticky;を指定した要素は「スティッキーアイテム」、親要素は「スティッキーコンテナ」と言います。この2種類が必須なのですが、他にも兄弟要素がないとうまく動きません。
理由としては、兄弟要素がない場合は「スティッキーコンテナの中で唯一の要素になってしまうため要素の高さが生まれないから」です。スティッキーアイテムとスティッキーコンテナの高さが同じなので固定できないということになります。そのため兄弟要素を入れてあげることで、その兄弟要素の高さの分スティッキーコンテナの高さが増えるのでその高さ分のスクロールは追従されるようになります。

極端な話、兄弟要素うんぬんよりも「スティッキーコンテナ」の高さがあれば機能するので、CSSでheightを指定すれば要素が「スティッキーアイテム」のみでも追従はします。

<div class="sticky-container">
  <div class="sticky">追従させたい要素</div>
</div>
<!-- /.sticky-container -->

<style>
.sticky-container{
  /* 兄弟要素がなくても高さをつけてあげると追従はする */
  height:100vh;
}
</style>

親要素・祖先要素にoverflow(hiddenやautoなど)が指定されていない

親要素やその親、さらにその祖先まで辿ってoverflow:hidden;overflow: auto;が指定されていると動きません。

<div class="wrapper">
  <div class="content">
    <div class="sticky-container">
      <div class="sticky">追従させたい要素</div>
      <div class="sticky-next">兄弟要素</div>
    </div>
    <!-- /.sticky-container -->
  </div>
  <!-- /.content -->
</div>
<!-- /.wrapper -->

<style>
.content{
  /* 親要素にこのような指定があると動かない */
  overflow:hidden;
}
.wrapper{
  /* 祖先要素も同じく指定があると動かない */
  overflow:hidden;
}
</style>

これは意外と見落としがちな部分ですが、動かない時は祖先要素にoverflow:hidden;をかけていることがよくあります。
対処法としては、overflow:visible;にする、もしくは記述を削除しましょう。

上記の動かない条件を踏まえてデモを作りました。

position:sticky;の動かない条件のデモ

以上の2つが動かなくなる条件ですが、この2つをクリアできない場合はCSSで実装するのはあきらめてJSで実装した方が良いと思います。

position:sticky;のサポートブラウザ

Can I use... position:sticky;のブラウザ対応状況

主要なブラウザではIE11以外すべて使えます。(2019.12.12現在)
詳細はCan I use...で確認してください。

IE11対応

stickyfillというpolyfillがあります。

yarnもしくはnpmでダウンロードして使うか

//npm
npm install stickyfilljs --save

//Yarn
yarn add stickyfilljs

//JS内でimport
import Stickyfill from 'stickyfilljs';

CDNでも読み込めます。

<script src="https://cdnjs.cloudflare.com/ajax/libs/stickyfill/2.1.0/stickyfill.min.js" crossorigin></script>

読み込んだら、position:sticky;を指定した要素に適用します。

const elements = document.querySelectorAll('.sticky');
Stickyfill.add(elements);

jQueryの場合はこう。

const elements = $('.sticky');
Stickyfill.add(elements);

最初に作ったデモサイトStickyfillを適用しているのでIE11でも動作します。

これで主要なブラウザで問題なく使えますね。

おわりに

今回は記事を書くに当たってスティッキーヘッダーの簡単なデモを作ってみました。
使い方によってはもっとおもしろいUIが作れそうなCSSプロパティだと思います。 ただ実際はJSの方が細かな調整が効くので、使い所はかなり限られる印象ですが、CSSしか使えない状況であったり、JSで全く同じ挙動を実装するならCSSの方が軽くできるという部分でのメリットがあるので、知っておいて損はないと思います。