Наблюдатель за перекрестком | Создание бесконечной прокрутки в вашем веб-приложении

Nov 25 2022
Если вы не пещерный человек или не ненавидите Джеффа Безоса (не имеет значения, даже если вы его ненавидите), вы, должно быть, совершали покупки в Интернете. Когда вы ищете товар в этих интернет-магазинах, вам предоставляется список результатов.
Фото SpaceX на Unsplash

Если вы не пещерный человек или не ненавидите Джеффа Безоса (не имеет значения, даже если вы его ненавидите), вы, должно быть, совершали покупки в Интернете. Когда вы ищете товар в этих интернет-магазинах, вам предоставляется список результатов. Если вы не такой странный, как я, и не ищете слишком конкретный « зуб тигра, пропитанный человеческой кровью» , есть шанс, что ваш поисковый запрос найдет тысячи продуктов. Как вы думаете, ваш браузер загружает все эти 1000+ элементов вместе? Нет , это было бы слишком много данных для браузера, чтобы обрабатывать и вычислять пользовательский интерфейс. Тем не менее, вы продолжаете получать предмет по мере того, как достигаете конца своего результата. Это называется бесконечной прокруткой .

Вот пример бесконечной прокрутки на главной странице YouTube.

Gif, объясняющий бесконечную прокрутку

Есть несколько методов для достижения этого с помощью JavaScript, но наиболее эффективным является использование приятного веб-API, называемого IntersectionObserver. Давайте посмотрим, как использовать этот 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);
})

Еще одним параметром конструктора является объект. Эти объекты обозначают опции для данного конкретного наблюдателя. В этом случае у нас есть только порог. порог здесь — это число, которое может принимать любое значение от 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);
});

Однако есть одна загвоздка. Когда мы прокручиваем вверх, поскольку у нас есть логика для переключения видимого класса, когда элемент пересекается, он также удаляет этот класс, когда это не так. Итак, когда мы прокручиваем вверх, мы видим, что наши карты исчезают сверху. Чтобы это исправить, мы перестанем наблюдать за картами, которые уже пересеклись.

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 в своих приложениях.