Функция Infinite Scroll в JavaScript: функция бесконечной прокрутки

В этом руководстве вы узнаете, как реализовать функцию бесконечной прокрутки JavaScript — Infinite Scroll.

Создание приложения

На следующем рисунке показано веб-приложение, которое мы собираемся создать:

Приложение JavaScript с бесконечной прокруткой

На странице отобразится список котировок, поступающих из API. По умолчанию показывается 10 котировок.

Если вы прокрутите страницу вниз, веб-приложение отобразит индикатор загрузки. Кроме того, он вызовет API, чтобы получить дополнительные котировки и добавить их в текущий список.

URL-адрес API, который вы собираетесь использовать, выглядит следующим образом:

https://api.javascripttutorial.net/v1/quotes/?page=1&limit=10

API принимает две строки запроса: page и limit. Эти строки запроса позволяют разбить цитаты с сервера на страницы.

Котировки разделены на страницы, определяемые строкой запроса page. И каждая страница имеет количество котировок, заданное параметром limit.

Страницы котировок

 

Создание структуры проекта

Сначала создайте новую папку с именем infinite-scroll. Внутри этой папки создайте две подпапки css и js.

Во-вторых, создайте style.css в папке css и app.js в папке js.

В-третьих, создайте новый файл HTML index.html в папке с infinite-scroll.

Окончательная структура папок проекта будет выглядеть так:

Структура проекта

Добавление кода в файл index.html

Откройте index.html и добавьте в него следующий код:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>JavaScript Infinite Scroll - Quotes</title>
    <link rel="stylesheet" href="css/style.css">
</head>
<body>

    <div class="container">
        <h1>Programming Quotes</h1>

        <div class="quotes">
        </div>

        <div class="loader">
            <div></div>
            <div></div>
            <div></div>
        </div>
    </div>
    <script src="js/app.js"></script>
</body>
</html>

В файле index.html поместите style.css в раздел head и app.js в раздел body.

В разделе body есть div с именем класса container. Элемент контейнера имеет четыре дочерних элемента:

  • Заголовок(h1), который показывает заголовок страницы.
  • Элемент div с quotes класса, который будет родительским элементом всех кавычек.
  • Загрузчик, отображающий индикатор загрузки. По умолчанию индикатор загрузки невидим.

Создание app.js

Далее используется querySelector() для выбора div с quotes класса и loader.

const quotesEl = document.querySelector('.quotes');
const loader = document.querySelector('.loader');

Функция getQuotes()

Следующая getQuotes() вызывает API и возвращает котировки:

const getQuotes = async(page, limit) => {
    const API_URL = `https://api.javascripttutorial.net/v1/quotes/?page=${page}&limit=${limit}`;
    const response = await fetch(API_URL);
    // handle 404
    if(!response.ok) {
        throw new Error(`An error occurred: ${response.status}`);
    }
    return await response.json();
}

Функция getQuotes() принимает два аргумента: page и limit. Она использует Fetch API для получения данных из API.

Поскольку fetch() возвращает промис, вы можете использовать синтаксис await для получения ответа. И вы вызываете метод json() объекта ответа, чтобы получить данные json.

getQuotes() возвращает обещание, которое будет преобразовано в данные JSON.

Поскольку getQuotes() использует ключевое слово await, она должна быть async.

Функция showQuotes()

Ниже определена showQuotes(), которая генерирует элементы <blockquote> из массива quotes и добавляет их к элементу quotes :

// show the quotes
const showQuotes = (quotes) => {
    quotes.forEach(quote => {
        const quoteEl = document.createElement('blockquote');
        quoteEl.classList.add('quote');

        quoteEl.innerHTML = `
            <span>${quote.id})</span>
            ${quote.quote}
            <footer>${quote.author}</footer>
        `;

        quotesEl.appendChild(quoteEl);
    });
};

Как это работает:

Функция showQuotes() использует метод forEach() для перебора массива quotes.

Для каждого объекта цитаты создается элемент <blockquote> с классом quote :

<blockquote class="quote">
</blockquote>

И он генерирует HTML-представление объекта цитаты, используя синтаксис литерала шаблона. Он добавляет HTML к элементу <blockquote>.

Ниже показан пример сгенерированного элемента <blockquote> :

<blockquote class="quote">
   <span>1)</span>
      Talk is cheap. Show me the code.
    <footer>Linus Torvalds</footer>
</blockquote>

В конце каждой итерации функция добавляет элемент <blockquote> к дочерним элементам элемента quotesEl с помощью appendChild().

Показать/скрыть функции индикатора загрузки

Ниже определяются две функции, которые отображают и скрывают элемент индикатора загрузки:

const hideLoader =() => {
    loader.classList.remove('show');
};

const showLoader =() => {
    loader.classList.add('show');
};

Индикатор загрузки имеет прозрачность 0, которая по умолчанию невидима. Класс .show устанавливает непрозрачность индикатора загрузки равным 1, что делает его видимым.

Чтобы скрыть индикатор загрузки, вы удаляете класс show из элемента индикатора загрузки. Точно так же, чтобы показать индикатор загрузки, вы добавляете класс show в его список классов.

Определить управляющие переменные

Следующее объявляет переменную currentPage и инициализирует ее единицей:

 let currentPage = 1;

Когда вы прокрутите страницу вниз до конца, приложение сделает запрос API для получения следующих котировок. Перед этим вам нужно увеличить переменную currentPage на единицу.

Чтобы указать количество котировок, которые вы хотите получить за раз, вы можете использовать такую константу:

const limit = 10;

Следующая переменная total хранит общее количество котировок, возвращенных из API:

let total = 0;

Функция hasMoreQuotes()

Следующая функция hasMoreQuotes() возвращает значение true, если:

  • Это первая выборка( total === 0 )
  • Или есть еще котировки, которые нужно получить из API( startIndex < total )
const hasMoreQuotes =(page, limit, total) => {
    const startIndex =(page - 1) * limit + 1;
    return total === 0 || startIndex < total;
};

Функция loadQuotes()

Следующий пример определяет функцию, которая выполняет четыре действия:

  • Показать индикатор загрузки.
  • Получить котировки из API, вызвав getQuotes(), если нужно получить больше котировок.
  • Показать цитаты на странице.
  • Скрыть индикатор загрузки.
// load quotes
const loadQuotes = async (page, limit) => {
    // show the loader
    showLoader();
    try {
        // if having more quotes to fetch
        if (hasMoreQuotes(page, limit, total)) {
            // call the API to get quotes
            const response = await getQuotes(page, limit);
            // show quotes
            showQuotes(response.data);
            // update the total
            total = response.total;
        }
    } catch (error) {
        console.log(error.message);
    } finally {
        hideLoader();
    }
};

Если getQuotes() выполняется очень быстро, вы не увидите индикатора загрузки.

Чтобы убедиться, что индикатор загрузки всегда отображается, вы можете использовать функцию setTimeout() :

// load quotes
const loadQuotes = async (page, limit) => {

    // show the loader
    showLoader();

    // 0.5 second later
    setTimeout(async () => {
        try {
            // if having more quotes to fetch
            if (hasMoreQuotes(page, limit, total)) {
                // call the API to get quotes
                const response = await getQuotes(page, limit);
                // show quotes
                showQuotes(response.data);
                // update the total
                total = response.total;
            }
        } catch (error) {
            console.log(error.message);
        } finally {
            hideLoader();
        }
    }, 500);

};

При добавлении функции setTimeout() индикатор загрузки будет отображаться как минимум полсекунды. И вы можете настроить задержку, изменив второй аргумент функции setTimeout().

Прикрепите событие прокрутки

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

Обработчик события прокрутки вызовет loadQuotes(), если выполняются следующие условия:

  • Во-первых, позиция прокрутки находится внизу страницы.
  • Во-вторых, есть больше котировок для получения.

Обработчик события прокрутки также увеличит переменную currentPage перед загрузкой следующих котировок.

 window.addEventListener('scroll',() => {
    const {
        scrollTop,
        scrollHeight,
        clientHeight
    } = document.documentElement;

    if(scrollTop + clientHeight >= scrollHeight - 5 &&
        hasMoreQuotes(currentPage, limit, total)) {
        currentPage++;
        loadQuotes(currentPage, limit);
    }
}, {
    passive: true
});

Инициализация страницы

Когда страница загружается в первый раз, вам нужно вызвать loadQuotes() для загрузки первой партии котировок:

loadQuotes(currentPage, limit);

Отображение кода app.js в IIFE

Чтобы избежать конфликта определенных вами переменных и функций, вы можете поместить весь код в файл app.js во IIFE.

Окончательный app.js будет выглядеть так:

(function () {

    const quotesEl = document.querySelector('.quotes');
    const loaderEl = document.querySelector('.loader');

    // get the quotes from API
    const getQuotes = async (page, limit) => {
        const API_URL = `https://api.javascripttutorial.net/v1/quotes/?page=${page}&limit=${limit}`;
        const response = await fetch(API_URL);
        // handle 404
        if (!response.ok) {
            throw new Error(`An error occurred: ${response.status}`);
        }
        return await response.json();
    }

    // show the quotes
    const showQuotes = (quotes) => {
        quotes.forEach(quote => {
            const quoteEl = document.createElement('blockquote');
            quoteEl.classList.add('quote');

            quoteEl.innerHTML = `
            <span>${quote.id})</span>
            ${quote.quote}
            <footer>${quote.author}</footer>
        `;

            quotesEl.appendChild(quoteEl);
        });
    };

    const hideLoader = () => {
        loaderEl.classList.remove('show');
    };

    const showLoader = () => {
        loaderEl.classList.add('show');
    };

    const hasMoreQuotes = (page, limit, total) => {
        const startIndex = (page - 1) * limit + 1;
        return total === 0 || startIndex < total;
    };

    // load quotes
    const loadQuotes = async (page, limit) => {

        // show the loader
        showLoader();

        // 0.5 second later
        setTimeout(async () => {
            try {
                // if having more quotes to fetch
                if (hasMoreQuotes(page, limit, total)) {
                    // call the API to get quotes
                    const response = await getQuotes(page, limit);
                    // show quotes
                    showQuotes(response.data);
                    // update the total
                    total = response.total;
                }
            } catch (error) {
                console.log(error.message);
            } finally {
                hideLoader();
            }
        }, 500);

    };

    // control variables
    let currentPage = 1;
    const limit = 10;
    let total = 0;


    window.addEventListener('scroll', () => {
        const {
            scrollTop,
            scrollHeight,
            clientHeight
        } = document.documentElement;

        if (scrollTop + clientHeight >= scrollHeight - 5 &&
            hasMoreQuotes(currentPage, limit, total)) {
            currentPage++;
            loadQuotes(currentPage, limit);
        }
    }, {
        passive: true
    });

    // initialize
    loadQuotes(currentPage, limit);

})()
Рейтинг
( Пока оценок нет )
Александр Русаков / автор статьи
Программист, разработчик, 12 лет опыта работы в крупных компаниях. Быстро освоил typescript, делюсь своими знаниями на страницах этого сайта.
Загрузка ...
JavaScript и TypeScript