scrollに応じて要素を固定/解除するCSS「position: sticky」とpolyfill「sticky-state」の紹介(脱jQuery)

当ブログでは、画面をスクロールすると途中でサイドバーを固定/解除する機能を実装しています。もともとこの機能をJavaScript(jQuery)で独自に実装していました。今回のこのサイドバー固定/解除機能をCSSのposition: stickyで実装し直すことにしました。いわゆる脱jQueryです。スクロールが絡むJavaScriptの処理は、イベントが頻発するためにブラウザに負荷をかけやすいです。今回CSSの「position: sticky」で実装し直したことで、ブラウザに優しいページになったんじゃないかなと思っています。今回はこのposition: sticky」でのサイドバー固定機能の実装方法と、polyfill「sticky-state」の使い方を紹介したいと思います。

はじめに

当ブログでは、ページをスクロールすると途中で「サイドバー」が画面に固定されるようになっています。実際にパソコンのブラウザで見てスクロールしてもらえればわかるかと思います。これまで、この機能をJavaScriptで実装していました。

ところがこれが原因で、ある時からFirefoxの開発者ツールのコンソールに以下のような警告が出るようになりました。

このサイトはスクロールに対して配置を固定する効果が使用されています。これは非同期パンで正しく動作しない可能性があります。詳細は https://developer.mozilla.org/docs/Mozilla/Performance/ScrollLinkedEffects を参照し、関連するツールと機能の議論に参加してください。

このFirefoxの警告に関してFirefox サイト互換性情報というサイトに以下の記述があります。

Firefox 46 以降、scroll イベントリスナー内でスクロール位置プロパティを変更しているページスクリプトに対し、ウェブコンソールが警告を表示するようになりました。そうしたイベントハンドラーは、要素の固定、スクロールスナップ効果、あるいはパララックス効果に利用されることがしばしばありますが、Firefox に実装中の非同期スクロール機能と相性が悪い場合があります。詳細と考えられる回避策については、MDN の Scroll-Linked Effects ページを参照してください。

警告のところにも掲載されているMDNのScroll-Linked Effectsのページを見ると、スクロールに応じて要素を固定する機能を実装する例として、CSSの「position: sticky」が紹介されていました。今回この「position: sticky」を使って、当ブログのサイドバーを固定する機能を実装し直すことにしました。実装は驚くほど簡単でした。以下に実装方法を紹介しますので、ぜひ参考にしてもらえればと思います。

position: stickyについて

position: stickyの使い方

「position: sticky」の使い方は実にシンプルです。固定したい要素に対して、position: stickyと位置(top, bottom, right, leftのうち少なくとも1つで閾値を指定)を指定するだけです。

例えばスクロール中に固定したい要素にstickyというclass名をつけたとしたら、以下のようにcssを指定します。

.sticky {
  position: -webkit-sticky; /* safari対応 */
  position: sticky;
  top: 10px; /* 上端から10pxのところで固定 */
}

上記は閾値としてtop: 10pxと指定したので、画面を下方向にスクロールした際に、その要素が上端から10pxの位置に到達するまではposition: relativeのように振る舞い、上端10pxの位置に到達するとそこで固定されます(position: fixedのように振る舞います)。

ちなみにposition: stickyによる「要素の固定」が有効となるのは、position: stickyを指定している要素の親要素の表示範囲内となります。つまり、その親要素の表示が画面から外れたり、親要素の画面上の表示部分の高さがposition: stickyを指定している要素の高さより小さくなったりしたタイミングで、固定状態は解除されます。(この辺りの挙動は実際に実装して試してみてください。)

閾値にはleftrightも指定することができるので、横にスクロールした際に要素を固定することも可能です。

position: stickyの実装例

それでは、実際に実装方法について紹介します。

まずHTMLは、以下のように固定/解除する要素と、その親要素を用意します。それぞれstickywrapperというclass名をつけます。class名は自由につけてかまいません。

<body>
  <!-- 親要素 -->
  <div class="wrapper">
    <!-- 固定/解除したい要素 -->
    <div class="sticky"></div>
  </div>
</body>

次にCSSですが、固定/解除する要素(.sticky)に、position: sticky;と閾値となるtop: 0;left: 0;を指定しました。それから親要素(.wrapper)は画面にスクロールが発生するように、幅と高さをそれぞぞれ200vw200vhとしました。

body {
  padding: 40px;
  width: 400vw;
  height: 400vh;
}
.wrapper {
  background-color: #ccc;
  padding: 300px;
  height: 200vh;
  width: 200vw;
}
.sticky {
  background-color: #009688;
  width: 50vw;
  height: 50vh;
  position: -webkit-sticky; /* Safariに対応する */
  position: sticky; /* 要素を固定/解除する */
  top: 0; /* 縦方向の閾値 */
  left: 0; /* 横方向の閾値 */
}

上記を実装したものが以下となります。実際に上下、左右にスクロールし、どのようにposition: stickyが機能するか確認してみてください。

https://codepen.io/maechabin/full/QvJBdP/

position: stickyが効かない場合

自分もハマった部分ですが、親要素にoverflow: hidden|scroll|autoを指定している場合は、position: stickyが効かないようです。現状は、overflow: visibleを指定している時のみ機能するようです(visibleが初期値なので、overflowを指定していない時も機能します)。

position: stickyのブラウザ対応状況

position: stickyのブラウザ対応状況は2017年5月現在以下の通りとなっています。

css position: sticky;
CSS position:sticky – Can I use…

惜しいところですが、IEとEdgeでは使用できません。ということで、残念ながら、position: stickyはまだそのまま使うことはできません。polyfillと併用する必要があります。

polyfillとして「sticky-state」を使う

そこで紹介したいのがposition: stickyのpolyfillの一つである「sticky-state」です。

このpolyfillは、jQueryを使用せず使えます。それがこのpolyfillをオススメする理由でもあります。早速ですが「sticky-state」の使い方を以下に紹介します。なお、こちらは多少JavaScriptを書く必要があります。

「sticky-state」のインストール

開発環境にNode.jsがインストールされている場合は、以下のコマンドでプロジェクトディレクトリ内に「sticky-state」をインストールすることができます。

npm install sticky-state --save

直接GitHubのページからダウンロードしても構いません。GitHubのページにアクセスすると、右上にClone or downloadと書かれている「緑色のボタン」が表示されていますので、そちらから「Download ZIP」をクリックしてダウンロードしてください。「sticky-state-master.zip」というzipファイルがダウンロードされます。

zipファイルを解凍すると、sticky-state-masterというフォルダが作られます。使用するファイルはsticky-state-masterフォルダ内のdistフォルダの中に入っています。

  • stick-state.css
  • stick-state.js
  • stick-state.min.js

ページへの読み込み

webpackやbrowserifyなどのビルドツールを使用して開発している場合は、以下をjsファイルの一番上に記述してページに読み込みます。

const StickyState = require('sticky-state');

script要素を使ってページにhtmlファイルに読み込む場合は、以下をhtmlファイルに記述します(ファイルのパスはお使いの環境に合わせてください)。

<script src="./dist/stick-state.min.js"></script>

JavaScriptの実装

JavaScriptの実装は以下の通りです。詳細はコメントをみてください。

// position: stickyがブラウザで使えるかチェックするための関数
function detectSticky() {
  const div = document.createElement('div');
  div.style.position = 'sticky';
  // position: stickyがブラウザで使えればtrue、使えなければfalseを返す
  return div.style.position.indexOf('sticky') !== -1;
}

// .stickyが指定されている要素に対してposition: stickyを適用させる関数
function callStickyState() {
  // position: stickyを適用させたい要素を引数に指定し、
  // StickyStateをnewしてインスタンスを返す
  return new StickyState(document.querySelectorAll('.sticky'));
}

// もしブラウザでposition: stickyが使えなければ、
// callStickyState関数を呼び出す
if (!detectSticky()) {
  callStickyState();
}

CSSの実装

CSSの実装は以下の通りです。こちらは付属のstick-state.cssファイルの内容そのままです。なお、以下は、position: stickyを適用させたい要素のclass名をstickyとした場合です。別のclass名にした場合は、書き換える必要があります。

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

.sticky.sticky-fixed.is-sticky {
  position: fixed;
  -webkit-backface-visibility: hidden;
  -moz-backface-visibility: hidden;
  backface-visibility: hidden;
}

.sticky.sticky-fixed.is-sticky:not([style*="margin-top"]) {
  margin-top: 0 !important;
}

.sticky.sticky-fixed.is-sticky:not([style*="margin-bottom"]) {
  margin-bottom: 0 !important;
}

.sticky.sticky-fixed.is-absolute {
  position: absolute;
}

polyfillの「sticky-state」の実装はこれで完了です。

まとめ

以上、position: stickyを使うと、CSSだけでスクロール追従して固定、解除する要素を実装することができます。JavaScriptで実装するよりも簡単で、さらにパフォーマンス的にもよいということなので、ぜひお試ししてもらえればと思います。まだposition: stickyはIEとEdgeでは対応していませんが、今回紹介したpolyfill「sticky-state」の使用も検討してもらえればと思います。この「sticky-state」はjQueryを使う必要がないという点からしても、自分ではオススメかなと思っています。

今回紹介したposition: stickyのように、CSSだけで実装できる機能が増えてきています。当ブログでは、スムーズスクロールもCSSのscroll-behaviorというプロパティを使って実装しています。こちらについても次回以降に紹介していくつもりです。ブラウザでできることが増え、ページの表示時だけでなく、表示後もパフォーマンスを気にする必要が出てきています。CSSでできることも増えてきていますし、状況に応じてCSSやJSを使い分けていく知識が求められてきていますね。

以下の書籍はそのような要望に答えてくれる内容となっています。最後に紹介しておきます。

Webフロントエンド ハイパフォーマンス チューニング
  • 『Webフロントエンド ハイパフォーマンス チューニング』
  • 著者: 久保田 光則
  • 出版社: 技術評論社
  • 発売日: 2017年5月26日

コメント一覧

  • 必須

コメント