スクロール検知をもっと便利に Intersection Observer API
一定の位置までスクロールされたら、クラスを付与(または削除)する、という挙動を実装するには、従来の方法であれば、JavaScriptで位置の取得が必要でした。
JSでスクロールを取得するには、常に位置情報を監視し続ける必要があるので、負荷が気になるんですよね。
そんなときに便利なのがIntersection Observer API(交差セクションオブザーバーAPI)です。
Intersection Observer APIの概要
技術的な解説は以下のページが参考になります。
使い方の解説なども記載がありますので、一度目を通しておくと良いです。
Intersection Observer APIの役割は明確でわかりやすいんですが、コードは結構わかりづらいんですよね。
Intersection Observer APIを使う
このIntersection Observer APIを何に使うか。
私は仕事でLPを作るのですが、LPの下部に固定のCTA(コールトゥーアクション:申し込みボタンとか)を設置することを依頼されます。
その時にFV(ファーストビュー)を超えてから、固定ボタンを表示してほしい、と依頼を受けます。
ということで、それを想定してサンプルを作りたいと思います。
ちなみに似たようなものをこのページでも使用しています。
FVを超えたら固定CTAを表示する設計
必要な物が、CTAボタン、FV要素。
FVがページ上部に到達したら、固定CTAを表示するという仕様で作ります。
サンプルの挙動解説
FVと書かれた画像が、ウィンドウの上部に全部到達すると、CTAというボタンが表示されます。
Intersection Observer APIの「intersectionRatio」というプロパティを使うと、交差している量を計測できるのでそれを使っています。
このintersectionRatioの値が「0」になったらCTAボタンが表示されます。
intersectionRatioの値を「ページサンプル」という画像の下に表示するようにしています。
挙動サンプル


IntersectionRatio:

HTMLのサンプルコード
上記サンプルのHTMLコードです。
<div id="sample"> <div id="fv"> <img src="img/fv.png" width="640" height="960" loading="lazy"> </div> <img src="img/page.png" width="640" height="250" loading="lazy"> <p class="mt_20">IntersectionRatio:<span id="ir_value"></span></p> <div id="sample_cta"> <img src="img/cta.png" width="640" height="117" loading="lazy"> </div> </div>
サンプルのJavaScriptコード
上記サンプルのJavaScriptのコードを表示します。
ベースは先ほど紹介しているMDNをカスタマイズしたものです。
let target_item = document.querySelector("#fv"); let cta = document.querySelector("#sample_cta"); window.addEventListener("load", (event) => { sample_createObserver(); }, false); function sample_createObserver() { let sample_observer; let options = { root: null, rootMargin: "-70px", threshold: 0 }; sample_observer = new IntersectionObserver(cta_disp, options); sample_observer.observe(target_item); } function cta_disp(entries){ entries.forEach(entry => { if (entry.intersectionRatio <= 0) { cta.classList.add("sample_on"); }else{ cta.classList.remove("sample_on"); } }); }
サンプルコードの解説
実際に表示しているサンプルは、intersectionRatioの表示などもしているのですが、そこは省いて紹介します。
1.変数の定義
最初にFVとCTAの定義をしています。
let target_item = document.querySelector("#fv"); let cta = document.querySelector("#sample_cta");
2.実行関数の呼び出し
次にページが読み込まれたら、window.addEventListenerで、sample_createObserver()という関数を呼び出します。
window.addEventListener("load", (event) => { sample_createObserver(); }, false);
3.sample_createObserverの定義
IntersectionObserverの呼び出しと、そのオプションの設定をしています。
function sample_createObserver() { let sample_observer; let options = { root: null, rootMargin: "-70px", threshold: 0 }; sample_observer = new IntersectionObserver(cta_disp, options); sample_observer.observe(target_item); }
let sample_observer;で定義した変数に、
new IntersectionObserver(cta_disp, options);を代入して、IntersectionObserverを呼び出す。
new IntersectionObserverの引数に入っている「cta_disp」は閾値を超えたら実行する関数が入っています。
4.optionsの内容
細かい設定はMDNの解説を一読することをオススメします。
が、わかりづらいので、私なりの解釈で説明入れておきます。
let options = { root: null, rootMargin: "-70px", threshold: 0 };
root: nullについて
これはスクロールする要素を指定します。
今回はnullを指定しているので、既定のブラウザのビューポートになります。
root
ターゲットが見えるかどうかを確認するためのビューポートとして使用される要素です。指定されなかった場合、または null の場合は既定でブラウザーのビューポートが使用されます。MDNより引用
rootオプションを指定した場合、監視対象はrootの子要素でなければ動作しません。
null以外の指定方法
nullではなく、要素を指定する場合は、
root: document.querySelector("#fv"),
のように指定します。
rootMargin: "-70px",について
これは、交差地点のマージンを指定しています。
私のサイト(今見ているページ)には、ページ上部に固定のナビが入っているので、その分を抜いて計算させています。
-(マイナス)の値を入れると交差地点が下に下がります。
threshold: 0 について
対象の要素がどの程度表示されたらコールバックをさせるか、を指定する値になります。
もしかしたら間違っているかもですが、IntersectionRatioの値と同じと考えてよいかと思います。
「intersectionRatio」は前述しましたが、交差地点と監視対象がどの程度、交差しているかを測るプロパティです。
「intersectionRatio」が「0」になったら、「cta_disp」関数が呼び出されるという仕組みです。
サンプルでは、intersectionRatioの値を表示しているので参考にしてください。
交差が開始すると「1」以下の数値になり、交差し終わると「0」になります。
threshold:は複数指定できる
thresholdは複数個の値を指定できます。
複数個を指定する場合は、配列の書式で記述します。
threshold: [0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1]
このページで実際に表示しているサンプルは、0~1までを0.1で区切ってコールバックしています。
intersectionRatioを細かく表示するために、このようにしていると。
threshold:0 は完全にスクロールしたら発動
この「threshold」の値が「0」というのは、言い換えると「見えなくなったら発動」ということになります。
0の場合、完全にスクロールして見なくなったらコールバックが発動する、という意味です。
逆に「1」を指定すると、対象要素が完全に表示されている(1pxもスクロールエリアにかかっていない)状態で発動となります。
サンプルの場合、FVがスクロールし終わったらCTAを表示する、という目的なので「0」を指定しているというわけですね。
5.sample_observerの定義
解説が長くなったのでコードを再掲します。
8行目の「sample_observer = new IntersectionObserver(cta_disp, options);」の解説です。
function sample_createObserver() { let sample_observer; let options = { root: null, rootMargin: "-70px", threshold: 0 }; sample_observer = new IntersectionObserver(cta_disp, options); sample_observer.observe(target_item); }
new IntersectionObserver(cta_disp, options);で、「IntersectionObserver」を呼び出し、その引数で「cta_disp」関数と、「options」を指定します。
コールバックで呼び出す関数を指定する、と、オプションを指定する、と覚えればOK。
6.監視対象の指定
9行目の、sample_observer.observe(target_item); は、スクロールを監視する対象を指定します。
target_itemは以下のものを指定しています。
let target_item = document.querySelector("#fv");
「#fv」がスクロールの監視対象になっています。
7.コールバックする関数の解説
いよいよ最後の関数です。
コールバックされたときに実行する関数です。
function cta_disp(entries){ entries.forEach(entry => { if (entry.intersectionRatio <= 0) { cta.classList.add("sample_on"); }else{ cta.classList.remove("sample_on"); } }); }
function cta_disp(entries)の引数の「entries」の中身は「IntersectionObserverEntry」のオブジェクトの配列が入ります。
この内容を知りたい場合はconsole.logで表示させると確認できます。
色々な値が入っております。
entries配列の中身の一部
以下が実際に「entries」をconsole.logで表示させた配列の内容の一部です。
[IntersectionObserverEntry] 0: IntersectionObserverEntry boundingClientRect: DOMRectReadOnly {x: 111.5, y: -1262.0625, width: 840, height: 990, top: -1262.0625, …} intersectionRatio: 0 intersectionRect: DOMRectReadOnly {x: 0, y: 0, width: 0, height: 0, top: 0, …} isIntersecting: false isVisible: false rootBounds: DOMRectReadOnly {x: 70, y: 70, width: 1283, height: 1424, top: 70, …} target: div#fv time: 280.2000000178814 [[Prototype]]: IntersectionObserverEntry length: 1 [[Prototype]]: Array(0)
entries配列をforEachで展開して条件指定
entriesが配列なので、forEachで展開し、その配列の中にある「entry.intersectionRatio」の値に対してif文を指定。
if (entry.intersectionRatio <= 0) { cta.classList.add("sample_on"); }else{ cta.classList.remove("sample_on"); }
intersectionRatioの値が「0」以下の場合「sample_on」というCSSのクラスを付与し、0より大きい場合はそのクラスを削除しています。
▼指定しているCSSの内容はこちら。
#sample_cta{ visibility: hidden; } #sample_cta.sample_on{ visibility: visible; }
サンプルはCLSの発生が嫌だったので、visibilityで非表示化しています。
intersectionRatioの値が「0」以下なので、FV要素がページ内から完全にスクロールし終わったら、CTAが表示される、という仕組みですね。
Intersection Observer APIまとめ
ちゃんと理解して使おうとするとなかなかややこしいのですが、コード自体は短めで構造はシンプルなので使いやすいと思います。
私もちゃんとは理解してないのですが、この記事を書いて少し理解が深まりました。
常に監視し続けなくて良いのがメリットで、ユーザーの環境に負荷がかからず良いとおもいます。
使い方次第でいろいろと使い道がありそうなので、ぜひ使ってみてください。
主にrootMarginについて追記しました
rootMarginの考え方を中心に、スクロールに応じて画像が大きくなるサンプルを使った解説記事を追加しました。