Obserwator skrzyżowania | Tworzenie nieskończonego przewijania w Twojej aplikacji internetowej

Nov 25 2022
Jeśli nie jesteś jaskiniowcem lub nie nienawidzisz Jeffa Bezosa (nieważne, czy tak jest), na pewno robiłeś zakupy online. Gdy szukasz artykułu w tych sklepach internetowych, wyświetlana jest lista wyników.
Zdjęcie autorstwa SpaceX na Unsplash

Jeśli nie jesteś jaskiniowcem lub nie nienawidzisz Jeffa Bezosa (nieważne, czy tak jest), na pewno robiłeś zakupy online. Gdy szukasz artykułu w tych sklepach internetowych, wyświetlana jest lista wyników. Jeśli nie jesteś dziwakiem jak ja i nie szukasz zbyt szczegółowego hasła „ tygrysi ząb nasączony ludzką krwią” , jest szansa, że ​​Twoje zapytanie wygeneruje tysiące produktów. Czy myślisz, że Twoja przeglądarka ładuje razem te ponad 1000 elementów? Nie , to byłoby zbyt dużo danych, aby przeglądarka mogła obsłużyć i obliczyć interfejs użytkownika. Mimo to nadal otrzymujesz przedmiot, gdy zbliżasz się do końca swojego wyniku. Nazywa się to nieskończonym przewijaniem .

Oto przykład przewijania w nieskończoność na stronie głównej YouTube.

Gif wyjaśniający nieskończone przewijanie

Istnieje kilka technik, aby to osiągnąć za pomocą JavaScript, ale najskuteczniejsza jest użycie słodkiego internetowego interfejsu API o nazwie IntersectionObserver. Zobaczmy, jak użyć tego interfejsu API do zaimplementowania naszej własnej mini nieskończonej przewijanej listy.

Koncepcja polega na obserwowaniu elementu HTML za każdym razem, gdy pojawia się on w rzutni, a następnie wyzwalaniu wywołania zwrotnego. Oto jak utworzyć tego obserwatora.

Stwórzmy plik html, który wygląda tak.

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

Najpierw pobierzmy wszystkie karty produktów według ich nazw klas

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

Kolejnym parametrem w konstruktorze jest obiekt. Obiekt ten oznacza opcje dla tego konkretnego obserwatora. W tym przypadku mamy tylko próg. próg jest tutaj liczbą, która może przyjąć dowolną wartość od 0 do 1. Oznacza to, jaki procent obserwowanego elementu powinien być widoczny w rzutni, aby można go było uznać za przecinający się. nadaliśmy jej wartość 0,95, co oznacza, że ​​gdy 95% dowolnej karty produktu pojawi się w oknie roboczym, karta ta zostanie uznana za przecinającą okno robocze.

Wróćmy do naszego pliku index.html . Mamy też inną widoczną klasę stylu zdefiniowaną w tagu styled. Ten styl zastosowany do naszej karty produktu sprawi, że karta będzie ponownie widoczna z niewielką animacją ( właściwość przejściowa ).

Wprowadźmy zmiany w naszym obserwatorze tak, aby wszystkie karty produktów, które mają co najmniej 95% w oknie podglądu były widoczne, a reszta była ukryta.

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

Jest jednak jeden haczyk. Kiedy przewijamy w górę, ponieważ mamy logikę przełączania widocznej klasy, gdy element się przecina, usuwa ona również tę klasę, gdy nie jest. Tak więc, kiedy przewijamy w górę, widzimy, jak nasze karty znikają z góry. Aby to naprawić, przestaniemy obserwować karty, które już się przecięły.

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

Najpierw stworzymy kolejnego obserwatora, który będzie obserwował ostatnią kartę.

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

Tak to teraz wygląda!

Idealny nieskończony zwój!

Teraz w rzeczywistych aplikacjach, zamiast ładować kolejne karty, będziemy pobierać nowe dane za pomocą API.

Daj mi znać, jak możesz kreatywnie wykorzystać ten interfejs API w swoich aplikacjach.