Osservatore di intersezione | Creazione di scorrimento infinito nella tua applicazione web
Se non sei una persona delle caverne o non odi Jeff Bezos (non importa anche se lo fai) devi aver fatto acquisti online. Quando cerchi un articolo su questi negozi online, ti viene presentato un elenco di risultati. Se non sei strano come me e non stai cercando " dente di tigre imbevuto di sangue umano" che è troppo specifico, c'è la possibilità che la tua query di ricerca ottenga migliaia di prodotti. Pensi che il tuo browser carichi tutti questi oltre 1000 elementi insieme? No , sarebbe un dato troppo grande per il browser da gestire e calcolare l'interfaccia utente. Tuttavia, continui a ottenere oggetti man mano che raggiungi la fine del risultato. Questo si chiama scorrimento infinito .
Ecco un esempio di scorrimento infinito sulla home page di YouTube.
Esistono diverse tecniche per raggiungere questo obiettivo utilizzando JavaScript, ma la più efficiente utilizza una dolce API Web chiamata IntersectionObserver. Vediamo come utilizzare questa API per implementare un nostro mini elenco scorrevole infinito.
Il concetto è osservare un elemento html ogni volta che entra nel viewport e quindi attivare una richiamata. Ecco come creare questo osservatore.
Creiamo un file html che assomigli a questo.
<!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>
Per prima cosa prendiamo tutte le schede prodotto in base al nome della loro 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 altro parametro nel costruttore è un oggetto. Questi oggetti indicano le opzioni per questo particolare osservatore. In questo caso, abbiamo solo soglia. threshold qui è un numero che può assumere qualsiasi valore compreso tra 0 e 1. Questo indica la percentuale dell'elemento che stiamo osservando che dovrebbe essere visibile nel viewport affinché possa essere considerato intersecante. gli abbiamo assegnato un valore di 0,95, il che significa che quando il 95% di qualsiasi scheda prodotto entrerà nel viewport, quella scheda sarà considerata intersecante con il viewport.
Torniamo al nostro file index.html . Abbiamo anche un'altra classe di stile visibile definita nel tag styled. Questo stile, applicato alla nostra scheda prodotto, renderà nuovamente visibile la scheda con una piccola animazione ( proprietà transizione ).
Apportiamo modifiche al nostro osservatore in modo tale che tutte le schede prodotto che sono almeno al 95% nel viewport siano visibili e il resto sia nascosto.
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);
});
C'è un problema però. Quando scorriamo verso l'alto, poiché abbiamo una logica per attivare o disattivare la classe visibile quando l'elemento si interseca, rimuove anche questa classe quando non lo è. Quindi, quando scorriamo verso l'alto, vediamo le nostre carte scomparire dall'alto. Per risolvere questo problema, smetteremo di osservare le carte che si sono già intersecate.
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);
});
Per prima cosa, creeremo un altro osservatore che osserverà solo l'ultima carta.
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);
}
}
Ecco come appare adesso!
Pergamena infinita perfetta!
Ora, nelle applicazioni del mondo reale, invece di caricare più carte recupereremo nuovi dati utilizzando l'API.
Fammi sapere come puoi utilizzare questa API in modo creativo nelle tue applicazioni.