Кнопка «Показать ещё» на чистом JavaScript

В этой статье рассмотрим, как сделать кнопку «Показать ещё» на чистом JavaScript. С помощью этой кнопки будем подгружать товары с сервера через AJAX.
Как работает кнопка «Показать ещё»
Кнопка «Показать ещё» обычно применяется, когда у нас очень много контента и его нужно вывести не весь сразу, а постепенно (небольшими порциями). Для этого весь контент разбивается на страницы.

При этом сначала отображается только первая страница (на этом примере это две карточки) и кнопка «Загрузить ещё». Если пользователь хочет посмотреть ещё записи (следующую страницу), то он просто нажимает на кнопку.
Это похоже на постраничную навигацию, но только предыдущие записи остаются на месте, а новые добавляются после них. При этом обычно это на сайте реализуется с использованием AJAX, то есть без перезагрузки страницы.
Шаг 1. Написание backend-кода на PHP
Перед тем как перейти к разработке frontend-кода, необходимо сначала написать серверный сценарий, который будет обрабатывать наш запрос и возвращать ответ в формате JSON. Можно конечно и в формате HTML, но это уже будет очень простой случай, который в рамках этой статьи рассматривать не будем.
Серверный сценарий напишем, например, на PHP. Кроме этого, не забываем, чтобы он работал необходимо установить веб-сервер с поддержкой PHP на локальную машину.
Для написания кода на PHP создадим файл, например, с именем more.php
в корне сайта.

Данные в этом примере будем получать для простоты из подготовленного массива, а не из базы данных:
$items = [
[
'title' => '...',
'img' => '1.png',
],
[
'title' => '...',
'img' => '2.png',
],
// ...
];
Количество записей на одной странице будем определять с помощью константы LIMIT
:
const LIMIT = 2;
Сейчас у нас одна страница будет состоять из 2 записей. Всего записей (элементов в массиве) узнаем с помощью функции count
:
$total = count($articles);
Номер страницы (page
) будем передавать серверу через POST. В скрипте на PHP для получения этого значения воспользуемся глобальной переменной $_POST['page']
:
$page = (int)($_POST['page'] ?? 1);
Дополнительно на странице клиента будем отображать кроме самих данных ещё их оставшееся количество. Вычисление оставшегося количества записей будем выполнять следующим образом:
$remain = $total - $page * LIMIT;
Сами отдаваемые данные будем получать как срез массива с помощью array_slice
:
$data = array_slice($items, ($page - 1) * LIMIT, LIMIT);
Кроме этого, клиенту будем передавать шаблон, который затем с помощью JavaScript будем использовать для генерации HTML-кода карточки:
$template = <<<HTML
<div class="card">
<img class="card-img" src="{{img}}" alt="{{title}}">
<div class="card-title">{{title}}</div>
</div>
HTML;
Возвращать ответ будем в формате JSON. Для этого перед отправкой данных установим соответствующий заголовок:
header('Content-Type: application/json');
Отправлять клиенту будем следующий массив данных, который используя функцию json_encode
переведём в JSON:
echo json_encode([
'total' => $total,
'page' => $page,
'remain' => $remain,
'template' => $template,
'data' => $data,
]);
Теперь давайте проверим, что выведет полученный PHP-файл, сохранённый в корне сайта под именем more.php
. Для этого откроем браузер и в адресной строке введем путь к нему (в данном случае: http://localhost/more.php
):

В окне браузера мы видим JSON-код, то есть результат выполнения файла more.php
на сервере.
Шаг 2. Создание HTML-кода
Теперь, после того как серверный код работает, создадим HTML-страничку index.html
:

Все элементы, связанные с кнопкой «Показать ещё» обернём в .cards
:
<div class="cards">
<!-- ... -->
</div>
Начнем написание HTML-кода с разметки контейнера для карточек. Добавлять карточки в контейнер будем с помощью JavaScript, поэтому этот элемент у нас будет пустым:
<div class="cards">
<!-- Контейнер для карточек -->
<div class="cards-container"></div>
</div>
Кроме кнопки у нас ёще будет блок .cards-progress
, в котором расположим прогресс-бар и текстовую информацию о количестве показанных карточек:
<div class="cards">
<!-- ... -->
<!-- Индикатор количества загруженных карточек -->
<div class="cards-progress">
<div class="progress">
<div class="progress-bar"></div>
</div>
<div class="cards-progress-info">
<span class="cards-count">-</span> из <span class="cards-total">-</span>
</div>
</div>
</div>
Ну и сама кнопка:
<div class="cards">
<!-- ... -->
<!-- Кнопка «Загрузить ещё» -->
<button type="button" class="btn btn-more" data-page="1">
<svg class="icon"><use href="#icon-arrow-repeat"></use></svg>
Загрузить ещё
</button>
</div>
У кнопки имеется атрибут data-page
, который будем использования для хранения количества показанных страниц.
Кнопка у нас имеет svg-иконку, которую создали посредством <use href="#icon-arrow-repeat"></use>
. При этом сама она определена в <svg>
с использованием <symbol>
, который вставили сразу после открывающего тега <body>
:
<svg xmlns="http://www.w3.org/2000/svg" class="d-none">
<symbol id="icon-arrow-repeat" viewBox="0 0 16 16">
<path d="M11.534 7h3.932a.25.25 0 0 1 .192.41l-1.966 2.36a.25.25 0 0 1-.384 0l-1.966-2.36a.25.25 0 0 1 .192-.41zm-11 2h3.932a.25.25 0 0 0 .192-.41L2.692 6.23a.25.25 0 0 0-.384 0L.342 8.59A.25.25 0 0 0 .534 9z"></path>
<path fill-rule="evenodd" d="M8 3c-1.552 0-2.94.707-3.857 1.818a.5.5 0 1 1-.771-.636A6.002 6.002 0 0 1 13.917 7H12.9A5.002 5.002 0 0 0 8 3zM3.1 9a5.002 5.002 0 0 0 8.757 2.182.5.5 0 1 1 .771.636A6.002 6.002 0 0 1 2.083 9H3.1z"></path>
</symbol>
</svg>
В результате получился следующий HTML-код.
Шаг 3. Стилизация карточек и кнопки
Создавать CSS-код будем в отдельном файле main.css, который расположим рядом с index.html
. Подключение main.css
к index.html
выполним с помощью <link>
:
<link rel="stylesheet" href="main.css">
Вёрстку контейнера (.card-container
), в который мы будем добавлять карточки, выполним через флексы:
/* Контейнер для карточек */
.card-container {
display: flex;
flex-wrap: wrap;
gap: 1rem;
}
Расстояние между карточками зададим с помощью CSS-свойство gap
.

Карточка товара (.card
) у нас будет состоять из изображения (.card-img
) и заголовка (.card-title
). Для оформления самой карточки и её элементов будем использовать следующие стили:
/* Карточка товара */
.card {
flex: 1 0 150px;
border-radius: 0.5rem;
box-shadow: 0 0.125rem 0.25rem rgb(0 0 0 / 8%);
text-align: center;
padding: 1rem;
}
.card-img {
display: block;
max-width: 100%;
height: auto;
}
Стили для кнопки:
.btn-more {
display: flex;
color: #212529;
font-size: 1rem;
line-height: 1.5;
background-color: #f5f5f5;
font-family: inherit;
border-radius: 0.25rem;
border: 0 solid #f5f5f5;
cursor: pointer;
align-items: center;
transition: filter .15s ease-in-out;
padding: 0.375rem 0.75rem;
gap: 0.25rem;
margin-left: auto;
margin-right: auto;
}
.btn-more:disabled {
opacity: .65;
color: #212529;
background-color: #eee;
}
.icon {
display: block;
width: 1rem;
height: 1rem;
fill: currentcolor;
}
.btn-more-loading .icon {
animation: 0.75s linear infinite rotate;
}
@keyframes rotate {
to {
transform: rotate(360deg);
}
}
При нажатии на кнопку она будет переводиться в неактивное состояние (disabled
). Кроме этого, к ней будет добавляться с помощью JavaScript класс btn-more-loading
, который будет активировать CSS-анимацию, и иконка в кнопке будет непрерывно вращаться.

Стилизацию прогресс-бара и дополнительной информации, расположенного рядом с ним, выполним следующим образом:
.cards-progress {
max-width: 150px;
align-items: center;
display: flex;
gap: 0.5rem;
margin: 1rem auto 0.5rem;
font-size: 0.75rem;
color: rgba(33, 37, 41, 0.75);
}
.progress {
flex: 1 0 100px;
display: flex;
height: 0.25rem;
overflow: hidden;
background-color: #e9ecef;
}
.progress-bar {
background-color: #3f51b5;
transition: width 0.3s ease;
}
Прогресс-бар – это очень полезный элемент, который позволяет увидеть пользователям количество показанных и оставшихся карточек. Кроме этого, с помощью него они могут оценить, насколько большим является список и сколько времени им потребуется для просмотра всех элементов.
Вся разметка, связанная с кнопкой «Показать ещё» у нас находятся в корневом элементе .card
. Его стилизацию в рамках этого примера выполним так:
.cards {
max-width: 400px;
margin: 1rem auto;
}
То есть ограничим его ширине и выровняем по центру, а также добавим внешние отступы сверху и снизу.
Для скрытия элементов мы будем использовать класс d-none
:
/* Класс для скрытия элемента */
.d-none {
display: none;
}
В этом примере у нас он используется для скрытия кнопки «Показать ещё», когда все карточки уже отображены и загружать уже больше нечего. Добавляется он в этом случае к элементу с помощью JavaScript. Кроме этого, d-none
используется ещё в HTML-коде для скрытия <svg>
.
Шаг 4. Добавление функциональности к кнопке с помощью JavaScript
Всю функциональность, связанную с кнопкой на странице, будем выполнять с помощью JavaScript, который напишем без использования каких-либо библиотек. Для этого создадим файл main.js и подключим его к нашей HTML-странице:
<script defer src="maint.js"></script>
Начнём создания JavaScript с выборки кнопки:
const btnMoreElem = document.querySelector('.btn-more');
Теперь напишем асинхронную функцию, которая будет выполняться при нажатии на кнопку, то есть она у нас будет использоваться в качестве обработчика события click
:
const loadMoreCards = async (e) => {
try {
// получим элемент, на котором произошло событие, в данном случае кнопку
const targetElem = e.target;
// получим корневой элемент, в котором располагаются вся разметка, связанная с карточками и кнопкой
const cardsElem = targetElem.closest('.cards');
// сделаем кнопку неактивной
targetElem.disabled = true;
// добавим к кнопке класс btn-more-loading, который добавляет анимацию вращения для иконки
targetElem.classList.add('btn-more-loading');
// создадим новый объект класса FormData
const body = new FormData();
// добавим в body номер текущей страницы
body.append('page', targetElem.dataset.page);
// получим ответ от сервера
const response = await fetch('more.php', {method: 'POST', body});
// выполняем действия указанные в if, если ответ от сервера успешный
if (response.ok) {
// читаем ответ от сервера как JSON и преобразовываем его в объект JavaScript
const result = await response.json();
// выполняем код анонимной функции, переданной в качестве аргумента методу setTimeout, через 500мс
window.setTimeout(() => {
// скрываем кнопку загрузки, если все карточки показаны
result.remain <= 0 ? targetElem.classList.add('d-none') : null;
// устанавливаем для кнопки в атрибут data-page значение переменной page, увеличенное на 1
targetElem.dataset.page = ++result.page;
// создаём массив карточек для вставки на страницу
const cards = result.data.map((item) => {
let html = result.template;
Object.keys(item).forEach((field) => {
html = html.replaceAll(`{{${field}}}`, item[field]);
})
return html;
});
// устанавливаем в качестве содержимого элемента .cards-count количество показанных карточек
cardsElem.querySelector('.cards-count').textContent = result.total - result.remain;
// устанавливаем в качестве содержимого элемента .cards-total общее количество карточек
cardsElem.querySelector('.cards-total').textContent = result.total;
// обновляем ширину прогресс-бара, отвечающего за количество показанных карточек
cardsElem.querySelector('.progress-bar').style.width = `${(result.total - result.remain) / result.total * 100}%`;
// вставляем карточки в контейнер
document.querySelector('.card-container').insertAdjacentHTML('beforeend', cards.join(''));
// удаляем анимацию иконки, с помощью удаления класса btn-more-loading
targetElem.classList.remove('btn-more-loading');
// делаем кнопку доступной для нажатия
targetElem.disabled = false;
}, 500);
}
} catch (error) {
console.error(error);
}
}
Этот код выполняет в следующем порядке вот эти действия:
- переводит кнопку в неактивное состояние, чтобы пользователь не мог выполнить это действие ещё раз пока не завершится текущее;
- включает анимацию для иконки, расположенной в кнопке, посредством установки класса
btn-more-loading
; - отправляет с помощью fetch на сервер запрос методом POST и передаёт в его теле номер запрашиваемой страницы;
- ждёт ответа от сервера в формате JSON;
- отключает видимость кнопки «Загрузить ещё», если мы показали все записи;
- обновляет значение атрибута
data-page
у кнопки; - формирует HTML-код карточек, используя полученный шаблон от сервера и данные;
- вставляет сформированный HTML-код карточек в контейнер, используя метод
insertAdjacentHTML
; - обновляет прогресс-бар и количество показанных карточек;
- удаляет класс
btn-more-loading
, который использовался для включения анимации иконки; - переводит кнопку в активное состояние, то есть делаем её доступной для следующего нажатия;
Теперь созданную функцию loadMoreCards
укажем в качестве обработчика события click
:
btnMoreElem.addEventListener('click', loadMoreCards);
Формирование первой страницы будем выполнять на клиенте с помощью JavaScript. Для этого просто программным способом кликнем по кнопке «Загрузить ещё»:
btnMoreElem.click();
Скриншот, на котором приведён ответ сервера при нажатии на кнопку:

Заключение
Закончили! Мы успешно создали кнопку «Показать еще» на веб-странице:

Комментарии: 2
Спасибо за данный пример! Может быть поделитесь информацией, может быть вы это раннее реализовывали - при нажатии на "Показать еще" подгружаются карточки, и наш фокус не падает на них, то есть к ним нужно "доскролить"... как можно решить этот вопрос?
После того как карточки вы вставили на страницу, получите ту, которая должна быть видна пользователю и вызовите функцию
scrollIntoView()
: