В этом руководстве вы узнаете, как реализовать функцию бесконечной прокрутки JavaScript — Infinite Scroll.
- Создание приложения
- Создание структуры проекта
- Добавление кода в файл index.html
- Создание app.js
- Функция getQuotes()
- Функция showQuotes()
- Показать/скрыть функции индикатора загрузки
- Определить управляющие переменные
- Функция hasMoreQuotes()
- Функция loadQuotes()
- Прикрепите событие прокрутки
- Инициализация страницы
- Отображение кода app.js в IIFE
Создание приложения
На следующем рисунке показано веб-приложение, которое мы собираемся создать:
На странице отобразится список котировок, поступающих из 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); })()