Intersection Observer APIで実装する無限スクロール
こんにちは!
株式会社ラブグラフでエンジニアをしているけいすけと申します!
今回は JavaScript の Intersection Observer API を使って無限スクロールを実装する方法を紹介します。
Intersection Observer API とは
Intersection Observer API とは HTML 中の要素の見え具合をチェックして、指定した見え具合になったときに任意の処理を実行することができる API です。
これを使うと無限スクロールを簡単に実装できます。早速、サンプルを見てみましょう!
無限スクロールのサンプル(説明用)
下に Intersection Observer API を使った無限スクロールのサンプルを示します。API の使い方はこのあとに説明します。
ぜひ無限にスクロールしてみてください!
挙動は次のようになっています。
-
ol
要素の中にli
要素を5つ配置 -
ol
要素の後ろに JavaScript でdiv
要素を追加(「この要素の全体がビューポートに入ったらコールバックを呼ぶ」と書かれた赤い四角形) - この
div
要素の 100% がビューポート内に入ったときに、ol
要素の中にli
要素を5つ追加
3つ目の「div
要素の 100% がビューポート内に入ったときに」という部分で Intersection Observer API が活躍しています。
挙動を分かりやすく見せるために ol
要素の後ろに赤い四角形を表示していますが、最後にこの赤い四角形を非表示にしたサンプルも載せています。
ソースコード
このサンプルのソースコードを下に示します。
HTML
<ol>
<li>list item 1</li>
<li>list item 2</li>
<li>list item 3</li>
<li>list item 4</li>
<li>list item 5</li>
</ol>
CSS
/* オレンジの四角形 */
li {
height: 100px;
margin: 20px;
line-height: 100px;
background-color: orange;
color: white;
text-align: center;
}
/* 赤の四角形 */
ol + div {
height: 200px;
line-height: 200px;
background-color: red;
color: white;
text-align: center;
}
JavaScript
const list = document.querySelector('ol');
const observedElement = document.createElement('div');
list.insertAdjacentElement('afterend', observedElement); // ol 要素の後ろに div 要素を追加
observedElement.textContent = 'この要素の 100% がビューポートに入ったら li 要素を5つ追加';
const callback = (entries) => {
// li 要素の数を取得
const numberOfListItems = list.getElementsByTagName('li').length;
entries.forEach(entry => {
if (entry.isIntersecting) {
// ol 要素の中に li 要素を5つ追加
for (let i = 1; i <= 5; i++) {
const listItem = document.createElement('li');
listItem.textContent = `list item ${numberOfListItems + i}`;
list.appendChild(listItem);
}
}
});
};
const options = {
root: null,
rootMargin: "0px",
threshold: 1.0,
};
const observer = new IntersectionObserver(callback, options);
observer.observe(observedElement);
Intersection Observer API の使い方
Intersection Observer API は次の手順で使います。
-
callback
とoptions
を設定する -
IntersectionObserver
コンストラクターにcallback
とoptions
を渡してIntersectionObserver
オブジェクトを作る - 作ったオブジェクトの
observe
メソッドに監視したい要素を渡す
callback
監視する要素のリストを引数に持つ関数です。各要素は isIntersecting
というプロパティを持っていて、このプロパティは options
で指定された条件が満たされているときは true
に、それ以外の場合は false
になります。
上のサンプルの場合は監視している要素は赤い四角形(observedElement
)だけなので、callback
の引数は [observedElement]
になっています。
次のように IntersectionObserver
オブジェクトに監視する要素を2つ渡した場合は、callback
の引数は [observedElement1, observedElement2]
になります。
observer.observe(observedElement1);
observer.observe(observedElement2);
options
callback
が呼ばれるタイミングを制御するためのオブジェクトで次の3つのプロパティを持っています。
root
監視している要素がどのくらい見えているかを計算するためのビューポートとして使う要素です。
指定されなかった場合または null
の場合はブラウザのビューポートになります。
上のサンプルでは null
を指定したので、赤い四角形がブラウザのビューポート内にどれくらい見えているかを監視していたことになります。
rootMargin
root
の周りのマージンです。CSS の margin
と同じように "10px 20px 30px 40px"
(上、右、下、左)という具合に指定します。px ではなく % でも指定できます。デフォルト値は "0px 0px 0px 0px"
です。
例えば、 "0px 0px 500px 0px"
とした場合、root
が下に 500px 拡大されます。root
がブラウザのビューポートだった場合、ビューポートの下 500px の領域にある要素も見えていると判定されます。
threshold
監視している要素がどのくらい見えたときにコールバックを呼ぶか指定する値です。
0 以上 1 以下の単一の数値または 0 以上 1 以下の数値の配列です。
1
を指定した場合は監視している要素の 100% が見えたとき、0.5
を指定した場合は 50% が見えたとき、0
を指定した場合は監視している要素が 1px でも見えたらコールバックを呼びます。[0, 0.25, 0.5, 0.75, 1]
を指定した場合は、見える割合が 25% を超える度にコールバックが呼ばれます。デフォルト値は 0 です。
Intersection Observer API で一番難しいのはこの callback
と options
の使い方だと思います。使い方は説明しましたがこれだけを読んで理解するのはなかなか難しいと思うので、サンプルのソースコードを編集して挙動がどう変わるかぜひ見てみてください。
無限スクロールの実用的なサンプル
上の例では ol
要素の後ろに div
要素を追加して、挙動を分かりやすく見せるために赤い四角形を表示していました。
実際に無限スクロールを実装するときに上のサンプルのような赤い四角形を表示したくはないと思うので、非表示にしたサンプルを下に示します。
ソースコード
ソースコードも貼っておきます。説明用のサンプルとの違いは、ol
要素の後ろに追加した div
要素のスタイルを消して赤い四角形を非表示にした点と、options
の threshold
を 1.0
から 0.0
にした点だけです。
こうすることで ol
要素の後ろにある div
要素(幅も高さもない)が 1px でも見えたら li
要素を追加するようになっています。
CSS
/* オレンジの四角形 */
li {
height: 100px;
margin: 20px;
line-height: 100px;
background-color: orange;
color: white;
text-align: center;
}
/* 赤の四角形のスタイルは削除 */
JavaScript
const list = document.querySelector('ol');
const observedElement = document.createElement('div');
list.insertAdjacentElement('afterend', observedElement);
const callback = (entries) => {
const numberOfListItems = list.getElementsByTagName('li').length;
entries.forEach(entry => {
if (entry.isIntersecting) {
for (let i = 1; i <= 5; i++) {
const listItem = document.createElement('li');
listItem.textContent = `list item ${numberOfListItems + i}`;
list.appendChild(listItem);
}
}
});
};
const options = {
root: null,
rootMargin: "0px",
threshold: 0.0, // 1.0 から 0.0 に変更
};
const observer = new IntersectionObserver(callback, options);
observer.observe(observedElement);
最後に
ここまで読んでいただきありがとうございます!
無限スクロールを実装する機会があったらぜひ Intersection Observer API を使ってみてください。
参考
Discussion