Observador de Interseção | Criando rolagem infinita em seu aplicativo da web

Nov 25 2022
Se você não é uma pessoa das cavernas ou não odeia Jeff Bezos (não importa se odeia), você deve ter comprado online. Quando você pesquisa um item nessas lojas online, é apresentada uma lista de resultados.
Foto da SpaceX no Unsplash

Se você não é uma pessoa das cavernas ou não odeia Jeff Bezos (não importa se odeia), você deve ter comprado online. Quando você pesquisa um item nessas lojas online, é apresentada uma lista de resultados. Se você não é estranho como eu e não está procurando por “ dente de tigre embebido em sangue humano” , que é muito específico, há uma chance de sua consulta de pesquisa obter milhares de produtos. Você acha que seu navegador carrega todos esses mais de 1000 itens juntos? Não , isso seria dados demais para o navegador manipular e calcular a interface do usuário. Ainda assim, você continua obtendo itens à medida que chega ao final do seu resultado. Isso é chamado de rolagem infinita .

Aqui está um exemplo de rolagem infinita na página inicial do YouTube.

Gif explicando scroll infinito

Existem várias técnicas para conseguir isso usando JavaScript, mas a mais eficiente é usar uma API da web chamada IntersectionObserver. Vamos ver como usar essa API para implementar uma mini lista rolável infinita nossa.

O conceito é observar um elemento html toda vez que ele entra na janela de visualização e, em seguida, acionar um retorno de chamada. Aqui está como criar este observador.

Vamos criar um arquivo html parecido com este.

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

Primeiro, vamos obter todos os cartões de produto pelo nome da 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);
})

Outro parâmetro no construtor é um objeto. Esses objetos significam as opções para esse observador específico. Neste caso, temos apenas limite. limiar aqui é um número que pode assumir qualquer valor de 0 a 1. Isso significa a porcentagem do item que estamos observando que deve estar visível na viewport para que seja considerado uma interseção. nós atribuímos a ele um valor de 0,95, o que significa que quando 95% de qualquer cartão de produto vier na janela de visualização, esse cartão será considerado interseccionado com a janela de visualização.

Vamos voltar ao nosso arquivo index.html . Também temos outra classe de estilo visível definida na tag styled. Este estilo, quando aplicado ao nosso cartão de produto, tornará o cartão visível novamente com uma pequena animação ( propriedade de transição ).

Vamos fazer alterações em nosso observador de forma que todos os cartões de produto que estejam pelo menos 95% na viewport fiquem visíveis e o restante fique oculto.

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

Há um porém. Quando rolamos para cima, porque temos uma lógica para alternar a classe visível quando o item está em interseção, ele também remove essa classe quando não está. Então, quando rolamos para cima, vemos nossas cartas desaparecendo do topo. Para corrigir isso, vamos parar de observar as cartas que já se cruzaram.

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

Primeiro, criaremos outro observador que apenas observará o último cartão.

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

É assim que parece agora!

Pergaminho Infinito Perfeito!

Agora, em aplicações do mundo real, em vez de carregar mais cartões, vamos buscar novos dados usando a API.

Deixe-me saber como você pode usar essa API de forma criativa em seus aplicativos.