Observateur d'intersection | Création d'un défilement infini dans votre application Web

Nov 25 2022
Si vous n'êtes pas une personne des cavernes ou si vous ne détestez pas Jeff Bezos (peu importe si vous le faites), vous devez avoir fait des achats en ligne. Lorsque vous recherchez un article sur ces boutiques en ligne, une liste de résultats vous est présentée.
Photo de SpaceX sur Unsplash

Si vous n'êtes pas une personne des cavernes ou si vous ne détestez pas Jeff Bezos (peu importe si vous le faites), vous devez avoir fait des achats en ligne. Lorsque vous recherchez un article sur ces boutiques en ligne, une liste de résultats vous est présentée. Si vous n'êtes pas bizarre comme moi et que vous ne cherchez pas une " dent de tigre imbibée de sang humain" qui est trop spécifique, il y a une chance que votre requête de recherche obtienne des milliers de produits. Pensez-vous que votre navigateur charge tous ces éléments 1000 + au total? Non , ce serait trop de données pour que le navigateur puisse gérer et calculer l'interface utilisateur. Pourtant, vous continuez à obtenir des objets au fur et à mesure que vous atteignez la fin de votre résultat. C'est ce qu'on appelle le défilement infini .

Voici un exemple de défilement infini sur la page d'accueil de YouTube.

Gif expliquant le défilement infini

Il existe plusieurs techniques pour y parvenir en utilisant JavaScript, mais la plus efficace utilise une API Web douce appelée IntersectionObserver. Voyons comment utiliser cette API pour implémenter notre propre mini liste déroulante infinie.

Le concept consiste à observer un élément html à chaque fois qu'il apparaît dans la fenêtre d'affichage, puis à déclencher un rappel. Voici comment créer cet observateur.

Créons un fichier html qui ressemble à ceci.

<!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>

Récupérons d'abord toutes les fiches produit par leur nom de classe

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);
})

Un autre paramètre du constructeur est un objet. Ces objets signifient les options pour cet observateur particulier. Dans ce cas, nous n'avons qu'un seuil. seuil ici est un nombre qui peut prendre n'importe quelle valeur de 0 à 1. Cela signifie le pourcentage de l'élément que nous observons qui doit être visible dans la fenêtre pour qu'il soit considéré comme sécant. nous lui avons donné une valeur de 0,95, ce qui signifie que lorsque 95 % de n'importe quelle carte de produit viendra dans la fenêtre, cette carte sera considérée comme croisant la fenêtre.

Revenons à notre fichier index.html . Nous avons également une autre classe de style visible définie dans la balise de style. Ce style, lorsqu'il est appliqué à notre fiche produit, rendra la fiche visible à nouveau avec une petite animation ( propriété de transition ).

Apportons des modifications à notre observateur de sorte que toutes les fiches produits qui sont au moins à 95 % dans la fenêtre d'affichage soient visibles et que le reste soit masqué.

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);
});

Il y a cependant un hic. Lorsque nous faisons défiler vers le haut, parce que nous avons une logique pour basculer la classe visible lorsque l'élément se croise, cela supprime également cette classe lorsqu'elle ne l'est pas. Ainsi, lorsque nous faisons défiler vers le haut, nous voyons nos cartes disparaître du haut. Pour y remédier, nous arrêterons d'observer les cartes qui se sont déjà croisées.

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);
});

Premièrement, nous allons créer un autre observateur qui observera juste la dernière carte.

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);
    }
}

C'est à quoi ça ressemble maintenant!

Parchemin infini parfait !

Désormais, dans les applications du monde réel, au lieu de charger plus de cartes, nous allons récupérer de nouvelles données à l'aide de l'API.

Faites-moi savoir comment vous pouvez utiliser cette API de manière créative dans vos applications.