知っておくと便利! CSSだけで要素がスクロールできるかどうかを検出する方法
Post on:2023年10月3日
CSSだけで要素がスクロールできるかどうかを検出する方法を紹介します。
先日紹介したスクロール駆動アニメーション(Scroll-Driven Animations)はスクロール可能なオーバーフローがある場合にのみアクティブになるため、要素がスクロール可能かも検出することができます。
Solved by CSS Scroll-Driven Animations: Detect if an element can scroll or not
by Bramus!
下記は各ポイントを意訳したものです。
※当ブログでの翻訳記事は、元サイト様にライセンスを得て翻訳しています。
はじめに
スクロール駆動アニメーション(Scroll-Driven Animations)は、スクロール可能なオーバーフローがある場合にのみアクティブになります。そのため、要素がスクロール可能かどうかを検出するメカニズムとしても使用できます。
スペーストグルまたはスタイルクエリを組み合わせると、スクロール可能かどうかに基づいて要素をスタイル設定するために必要なものがすべて揃います。
スクロール駆動アニメーション(Scroll-Driven Animations)について、詳しくは下記をご覧ください。
CSSでの実装が大きく変わる! Scroll-driven Animations スクロールをトリガーにしたアニメーションを実装する方法
CSSで要素がスクロールできるかどうかを検出する
まずは、すぐにそのCSSが知りたい人用にCSSを。
1 2 3 4 5 6 7 8 9 10 11 12 |
@keyframes detect-scroll { from, to { --can-scroll: ; } } .container { animation: detect-scroll linear; animation-timeline: scroll(self); --bg-if-can-scroll: var(--can-scroll) lime; --bg-if-cant-scroll: red; background: var(--bg-if-can-scroll, var(--bg-if-cant-scroll)); } |
このCSSの使用方法、なぜこれが機能するのか知りたい人は、このまま読み進めてください😉
アクティブと非アクティブなスクロール駆動アニメーション
スクロール駆動アニメーション(Scroll-Driven Animations)は、スクロールによって駆動されるアニメーションです。しかし、アニメーションをするためのスクロール距離がない場合はどうなるでしょうか?
W3Cの仕様ではこれがカバーされており、アニメーションは非アクティブになると記載されています。
プリシンバルボックスが存在しないかスクロールコンテナでない要素である場合、またはスクロール可能なオーバーフローがない場合、ScrollTimelineは非アクティブになります。
これがどういうことなのか、見てましょう。
下記のCSSはhotpink
からlime
まで色を変化させるアニメーションです。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
.container { height: 250px; width: 250px; overflow-y: auto; animation: anim linear; animation-timeline: scroll(self); } @keyframes anim { from { color: hotpink; } to { color: lime; } } |
スクロール可能なオーバーフローを持たないスクロールコンテナでは、アニメーションはアクティブにならないので、テキストに設定された色が適用されます。
下記のデモでは、左のコンテナは最初はhotpink
でスクロールするとline
に変化し、右のコンテナは色が変化しません。
See the Pen
Scroll Detection with CSS Scroll-Driven Animations: Inactive Animation by coliss (@coliss)
on CodePen.
CSSの変数を追加して、スクロール検出機能を作成
CSSの通常のプロパティと同様に、CSSの変数(カスタムプロパティ)もアニメーションさせることができます。
上記のCSSをCSSの変数で記述してみます。
1 2 3 4 |
@keyframes anim { from { --foo: 0; } to { --foo: 1; } } |
スクロール可能なオーバーフローを持たないスクロールコンテナにスクロール駆動アニメーションとしてアタッチされた場合、CSSの変数の値は設定されていないため、初期値になります。
スクロール検出機能を持たせるには、アニメーションの内部で値が同じ値になるようにします。これは、to
とfrom
を同じ値に設定するだけです。
1 2 3 |
@keyframes anim { from, to { --can-scroll: 1; } } |
このアニメーションをスクロール駆動アニメーションにフックし、--can-scroll
の初期値が0
であることを確認すると、完全なコードは下記の通りです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
.container { height: 250px; width: 250px; overflow-y: auto; --can-scroll: 0; animation: detect-scroll; animation-timeline: scroll(self); } @keyframes detect-scroll { from, to { --can-scroll: 1; } } |
スクロール可能なオーバーフローを持つ要素ではアニメーションがアクティブになるので、--can-scroll
の値は1
になります。そして、スクロール可能なオーバーフローを持たない要素では値は0
になります。
この値は計算に使用できます。たとえば、下記のように使用します。
1 2 3 |
.container { outline: calc(10px * var(--can-scroll)) dotted lime; } |
このCSSは、スクロール可能なコンテナにlime
の点線でアウトラインが表示され、スクロール不可能な渾天に0pxのアウトラインが表示されます。
See the Pen
Scroll Detection with CSS Scroll-Driven Animations by coliss (@coliss)
on CodePen.
実装でより使いやすくする
実装での作業を簡単にするため、いくつかのバリエーションを用意しました。
- スペーストグルを使用する
- スタイルクエリを使用する
スペーストグルを使用する
スペーストグルの変数は、--can-scroll
の初期値にinitial
を設定し、アニメーション内の値としてスペースを設定することで、スペーストグルの基本的な仕組みに従います。これにより、数値以外の設定も可能になります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
.container { height: 250px; width: 250px; overflow-y: auto; --can-scroll: initial; /* initial = false */ animation: detect-scroll; animation-timeline: scroll(self); --color-if-can-scroll: var(--can-scroll) lime; --color-if-cant-scroll: red; outline: 10px dotted var(--color-if-can-scroll, var(--color-if-cant-scroll)); } @keyframes detect-scroll { from, to { --can-scroll: ; /* space = true */ } } |
この実装の唯一の欠点はスペーストグルの基本的な仕組みに慣れていないと、少しコードが読みにくいことです。
See the Pen
Scroll Detection with CSS Scroll-Driven Animations (Space Toggle) by coliss (@coliss)
on CodePen.
スタイルクエリを使用する
スタイルクエリの変数は、この値の変化に対応するためにスタイルクエリを使用します。これにより、スタイルクエリのポリフィルとしても機能します。
参考: CSSのスタイルクエリの基礎知識と使い方を解説、親要素の「スタイル」に応じて子要素のスタイルを適用
1 2 3 4 5 6 7 8 9 10 11 |
@container style(--can-scroll: 1) { p { color: lime; } } @container style(--can-scroll: 0) { p { color: red; } } |
この実装の欠点は、コンテナクエリの子要素にしかスタイルを設定できないこと、そしてこの記事の執筆時点ではChromeのみがスタイルクエリをサポートしていることです。
See the Pen
Scroll Detection with CSS Scroll-Driven Animations (Style Query) by coliss (@coliss)
on CodePen.
実装例
最近、Shu Ding氏によるスクロール駆動アニメーションを使用した素晴らしいデモを見ました。上下にスクロールするとスクロールインジケーターが表示・非表示になります。
See the Pen
Scroll Timeline by coliss (@coliss)
on CodePen.
素晴らしいデモですが、問題もあります。コンテンツがスクローラーに対して小さすぎる場合、インジケーターが上下共に表示されてしまいます。
See the Pen
Scroll Timeline (Short Content) by coliss (@coliss)
on CodePen.
この問題は、この記事で解説したCSSによるスクロール検出が役立ちます。スクロール可能なオーバーフローがある場合にのみインジケーターを表示すればよいのです。インジケーターが表示されていないときにレイアウトがずれないように、条件付きでvisibility
をhidden
に設定しておきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
@keyframes detect-scroll { from, to { --can-scroll: ;} } .container { animation: detect-scroll; animation-timeline: --scroll-timeline; } .up, .down { --visibility-if-can-scroll: var(--can-scroll) visible; --visibility-if-cant-scroll: hidden; visibility: var(--visibility-if-can-scroll, var(--visibility-if-cant-scroll)); } |
上記のCSSで問題が解決します。
See the Pen
Conditional Revealing Scroll Indicators Scroll-Timeline by coliss (@coliss)
on CodePen.
ついでに、CSSを再利用しやすいものにしました。reveal
キーフレームを0%
から2%
に制限するのではなく、0%
から100%
にして範囲全体をカバーするようにしました。次に、animation-range: 20px 40px;
を使用して、アニメーションを実行するタイミングを制限します。詳しくは、Xのスレッドをご覧ください。
また、インジケーターではなく、シャドウをつける素晴らしいデモもkizuのブログにあります。
sponsors