交差点オブザーバー | Web アプリケーションで無限スクロールを作成する

Nov 25 2022
あなたが洞窟の人ではないか、ジェフ・ベゾスが嫌いではない場合(嫌いでも構いません)、オンラインで買い物をしたに違いありません。これらのオンライン ショップでアイテムを検索すると、結果のリストが表示されます。
UnsplashのSpaceXによる写真

あなたが洞窟の人ではないか、ジェフ・ベゾスが嫌いではない場合(嫌いでも構いません)、オンラインで買い物をしたに違いありません。これらのオンライン ショップでアイテムを検索すると、結果のリストが表示されます。あなたが私のように奇妙ではなく、「人間の血に浸した虎の歯」を探していない場合は、検索クエリで何千もの製品が得られる可能性があります. あなたのブラウザはこれらの 1000 以上のアイテムをすべてロードすると思いますか? いいえ、ブラウザが処理してユーザー インターフェイスを計算するには、データが多すぎます。それでも、結果の終わりに到達し続けると、アイテムを取得し続けます。これは無限スクロールと呼ばれます。

YouTube ホームページの無限スクロールの例を次に示します。

無限スクロールを説明するGIF

JavaScript を使用してこれを実現する方法はいくつかありますが、 IntersectionObserverと呼ばれる便利な Web API を使用する最も効率的な方法です。この API を使用して、独自の小さな無限スクロール可能なリストを実装する方法を見てみましょう。

概念は、ビューポートに来るたびに html 要素を観察し、コールバックをトリガーすることです。このオブザーバーを作成する方法は次のとおりです。

このようなhtmlファイルを作成しましょう。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Infinte Scroll</title>
</head>
<style>
    .product {
        padding: 20px;
        border: 1px solid black;
        margin-bottom: 20px;
        opacity: 0;
        margin-left: 100px;
        transition: all 300ms;
    }
    .visible {
        opacity: 1;
        margin-left: 0px;
    }
</style>
<body>
    <div id="container">
        <div class="product">Product</div>
        <div class="product">Product</div>
        <div class="product">Product</div>
        <div class="product">Product</div>
        <div class="product">Product</div>
        <div class="product">Product</div>
        <div class="product">Product</div>
        <div class="product">Product</div>
        <div class="product">Product</div>
        <div class="product">Product</div>
        <div class="product">Product</div>
        <div class="product">Product</div>
        <div class="product">Product</div>
        <div class="product">Product</div>
        <div class="product">Product</div>
        <div class="product">Product</div>
        <div class="product">Product</div>
        <div class="product">Product</div>
        <div class="product">Product</div>
    </div>

    <script src="./index.js"></script>
</body>
</html>

まず、すべての製品カードをクラス名で取得しましょう

const cards = document.querySelectorAll('.product');

const productCards= document.querySelectorAll('.product');

const observer = new IntersectionObserver((entries) => {
    // Todo
}, {
    threshold: 0.95,
});

productCards.forEach((card) => {
  observer.observe(card);
})

コンストラクターのもう 1 つのパラメーターはオブジェクトです。これらのオブジェクトは、この特定のオブザーバーのオプションを示します。この場合、しきい値しかありません。ここでのしきい値は、0 から 1 までの任意の値を取ることができる数値です。これは、交差していると見なされるために、ビューポートに表示されるべきである、観察している項目のパーセンテージを意味します。0.95 の値を指定しました。これは、製品カードの 95% がビューポートに入ると、そのカードがビューポートと交差していると見なされることを意味します。

index.htmlファイルに戻りましょう。styled タグで定義された別のスタイル クラスも表示されます。このスタイルを製品カードに適用すると、少しのアニメーション (遷移プロパティ) でカードが再び表示されます。

ビューポートで少なくとも 95% の製品カードがすべて表示され、残りは非表示になるように、オブザーバーに変更を加えてみましょう。

const productCards = document.querySelectorAll('.product');

const observer = new IntersectionObserver((entries) => {
    entries.forEach(entry => {
        entry.target.classList.toggle('visible', entry.isIntersecting);
    })
}, {
    threshold: 0.95
});

productCards.forEach((card) => {
    observer.observe(card);
});

ただし、1 つのキャッチがあります。上にスクロールすると、アイテムが交差しているときに可視クラスを切り替えるロジックがあるため、交差していないときにこのクラスも削除されます。そのため、上にスクロールすると、カードが上から消えているのがわかります。これを修正するために、すでに交差したカードの監視を停止します。

const productCards = document.querySelectorAll('.product');

const observer = new IntersectionObserver((entries) => {
    entries.forEach(entry => {
        entry.target.classList.toggle('visible', entry.isIntersecting);
        // unobserving entries which have already intersected
        if (entry.isIntersecting) observer.unobserve(entry.target);
    })
}, {
    threshold: 0.95
});

productCards.forEach((card) => {
    observer.observe(card);
});

まず、最後のカードを監視する別のオブザーバーを作成します。

const lastCardObserver = new IntersectionObserver((entries) => {
    const lastCard = entries[0];
    if (!lastCard.isIntersecting) return;
    loadMoreCards();
    lastCardObserver.unobserve(lastCard.target);
    lastCardObserver.observe(document.querySelector('.product:last-child'));
}, {
    threshold: 0.95
});

lastCardObserver.observe(document.querySelector('.product:last-child'));

function loadMoreCards() {
    const container = document.getElementById('container');
    for (let i = 0; i < 10; i++) {
        const element = document.createElement('div');
        element.classList.add('product');
        element.innerText = 'Product';
        observer.observe(element);
        container.appendChild(element);
    }
}

これが今の姿です!

完璧な無限スクロール!

現在、実際のアプリケーションでは、より多くのカードをロードする代わりに、API を使用して新しいデータをフェッチします。

この API をアプリケーションでクリエイティブに使用する方法を教えてください。