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

Кнопка «Показать ещё» на чистом JavaScript
Содержание:
  1. Как работает кнопка «Показать ещё»
  2. Шаг 1. Написание backend-кода на PHP
  3. Шаг 2. Создание HTML-кода
  4. Шаг 3. Стилизация карточек и кнопки
  5. Шаг 4. Добавление функциональности к кнопке с помощью JavaScript
  6. Заключение
  7. Комментарии

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

Как работает кнопка «Показать ещё»

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

Разбивка контента на страницы, подгрузка страниц выполняется с помощью нажатия на кнопку «Показать ещё»

При этом сначала отображается только первая страница (на этом примере это две карточки) и кнопка «Загрузить ещё». Если пользователь хочет посмотреть ещё записи (следующую страницу), то он просто нажимает на кнопку.

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

Шаг 1. Написание backend-кода на PHP

Перед тем как перейти к разработке frontend-кода, необходимо сначала написать серверный сценарий, который будет обрабатывать наш запрос и возвращать ответ в формате JSON. Можно конечно и в формате HTML, но это уже будет очень простой случай, который в рамках этой статьи рассматривать не будем.

Серверный сценарий напишем, например, на PHP. Кроме этого, не забываем, чтобы он работал необходимо установить веб-сервер с поддержкой PHP на локальную машину.

Для написания кода на PHP создадим файл, например, с именем more.php в корне сайта.

Создание PHP-файла в текстовом редакторе Visual Studio Code

Данные в этом примере будем получать для простоты из подготовленного массива, а не из базы данных:

PHP
$items = [
  [
    'title' => '...',
    'img' => '1.png',
  ],
  [
    'title' => '...',
    'img' => '2.png',
  ],
  // ...
];

Количество записей на одной странице будем определять с помощью константы LIMIT:

PHP
const LIMIT = 2;

Сейчас у нас одна страница будет состоять из 2 записей. Всего записей (элементов в массиве) узнаем с помощью функции count:

PHP
$total = count($articles);

Номер страницы (page) будем передавать серверу через POST. В скрипте на PHP для получения этого значения воспользуемся глобальной переменной $_POST['page']:

PHP
$page = (int)($_POST['page'] ?? 1);

Дополнительно на странице клиента будем отображать кроме самих данных ещё их оставшееся количество. Вычисление оставшегося количества записей будем выполнять следующим образом:

PHP
$remain = $total - $page * LIMIT;

Сами отдаваемые данные будем получать как срез массива с помощью array_slice:

PHP
$data = array_slice($items, ($page - 1) * LIMIT, LIMIT);

Кроме этого, клиенту будем передавать шаблон, который затем с помощью JavaScript будем использовать для генерации HTML-кода карточки:

PHP
$template = <<<HTML
<div class="card">
  <img class="card-img" src="{{img}}" alt="{{title}}">
  <div class="card-title">{{title}}</div>
</div>
HTML;

Возвращать ответ будем в формате JSON. Для этого перед отправкой данных установим соответствующий заголовок:

PHP
header('Content-Type: application/json');

Отправлять клиенту будем следующий массив данных, который используя функцию json_encode переведём в JSON:

PHP
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:

Создание HTML-страницы в текстовом редакторе Visual Studio Code

Все элементы, связанные с кнопкой «Показать ещё» обернём в .cards:

HTML
<div class="cards">
  <!-- ... -->
</div>

Начнем написание HTML-кода с разметки контейнера для карточек. Добавлять карточки в контейнер будем с помощью JavaScript, поэтому этот элемент у нас будет пустым:

HTML
<div class="cards">
  <!-- Контейнер для карточек -->
  <div class="cards-container"></div>
</div>

Кроме кнопки у нас ёще будет блок .cards-progress, в котором расположим прогресс-бар и текстовую информацию о количестве показанных карточек:

HTML
<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>

Ну и сама кнопка:

HTML
<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>:

HTML
<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>:

HTML
<link rel="stylesheet" href="main.css">

Вёрстку контейнера (.card-container), в который мы будем добавлять карточки, выполним через флексы:

CSS
/* Контейнер для карточек */
.card-container {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
}

Расстояние между карточками зададим с помощью CSS-свойство gap.

Стилизация карточки товара

Карточка товара (.card) у нас будет состоять из изображения (.card-img) и заголовка (.card-title). Для оформления самой карточки и её элементов будем использовать следующие стили:

CSS
/* Карточка товара */
.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;
}

Стили для кнопки:

CSS
.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-анимацию, и иконка в кнопке будет непрерывно вращаться.

Стили для кнопки «Показать ещё» и прогресс-бара

Стилизацию прогресс-бара и дополнительной информации, расположенного рядом с ним, выполним следующим образом:

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:

CSS
/* Класс для скрытия элемента */
.d-none {
  display: none;
}

В этом примере у нас он используется для скрытия кнопки «Показать ещё», когда все карточки уже отображены и загружать уже больше нечего. Добавляется он в этом случае к элементу с помощью JavaScript. Кроме этого, d-none используется ещё в HTML-коде для скрытия <svg>.

Шаг 4. Добавление функциональности к кнопке с помощью JavaScript

Всю функциональность, связанную с кнопкой на странице, будем выполнять с помощью JavaScript, который напишем без использования каких-либо библиотек. Для этого создадим файл main.js и подключим его к нашей HTML-странице:

HTML
<script defer src="maint.js"></script>

Начнём создания JavaScript с выборки кнопки:

JavaScript
const btnMoreElem = document.querySelector('.btn-more');

Теперь напишем асинхронную функцию, которая будет выполняться при нажатии на кнопку, то есть она у нас будет использоваться в качестве обработчика события click:

JavaScript
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:

CSS
btnMoreElem.addEventListener('click', loadMoreCards);

Формирование первой страницы будем выполнять на клиенте с помощью JavaScript. Для этого просто программным способом кликнем по кнопке «Загрузить ещё»:

CSS
btnMoreElem.click();

Скриншот, на котором приведён ответ сервера при нажатии на кнопку:

Ответ сервера, который мы получаем, когда нажимаем на кнопку «Показать ещё»

Заключение

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

Результат создания кнопки «Показать еще» на чистом JavaScript

Демо

Комментарии: 2

Stark
Stark

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

Александр Мальцев
Александр Мальцев

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

// получаем нужную карточку
const cardEl = document.querySelector('.card');
// прокручиваем страницу так, чтобы карточка была видна пользователю
cardEl.scrollIntoView();