Гид по технологиям

Бесконечная прокрутка: реализация на HTML, CSS и JavaScript

4 min read Веб-разработка Обновлено 25 Apr 2026
Бесконечная прокрутка на HTML/CSS/JS
Бесконечная прокрутка на HTML/CSS/JS

Иллюстрация человека, удобно сидящего в современном кресле и использующего смартфон

Кратко

Бесконечная прокрутка подгружает новый контент по мере прокрутки страницы, улучшая плавность просмотра на мобильных устройствах. Эта статья показывает простой шаг‑за‑шаговый подход на чистом HTML, CSS и JavaScript с советами по производительности и доступности.

Введение

Бесконечная прокрутка (infinite scroll) позволяет подгружать элементы автоматически по мере прокрутки вниз, в отличие от классической пагинации, где пользователь нажимает кнопку или ссылку. Это удобно для лент контента, галерей и каталогов — особенно на мобильных устройствах — но требует аккуратного управления запросами и состояния загрузки.

Настройка фронтенда

Ниже — минимальная HTML-структура для демонстрации. Пример сохраняет подключение CSS и JavaScript.




    


    

Страница с бесконечной прокруткой

Куртка — изображение товара Куртка — изображение товара Куртка — изображение товара Куртка — изображение товара Куртка — изображение товара Куртка — изображение товара

Этот файл содержит контейнер с плейсхолдерными изображениями и ссылки на style.css и script.js.

Начальная страница после добавления HTML и CSS

Стили CSS для прокручиваемого контента

Чтобы расположить изображения в сетке, добавьте следующий CSS в файл style.css:

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

html { font-size: 62.5%; }

body {
  font-family: Cambria, Times, "Times New Roman", serif;
}

h1 {
  text-align: center;
  font-size: 5rem;
  padding: 2rem;
}

img {
  width: 100%;
  display: block;
}

.products__list {
  display: flex;
  flex-wrap: wrap;
  gap: 2rem;
  justify-content: center;
}

.products__list > * {
  width: calc(33% - 2rem);
}

.loading-indicator {
  display: none;
  position: absolute;
  bottom: 30px;
  left: 50%;
  background: #333;
  padding: 1rem 2rem;
  color: #fff;
  border-radius: 10px;
  transform: translateX(-50%);
}

После добавления вышеуказанных стилей страница должна выглядеть примерно так:

Базовая реализация на JavaScript

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

"use strict";

window.addEventListener("scroll", () => {
  if (
    window.scrollY + window.innerHeight >=
      document.documentElement.scrollHeight - 100
  ) {
    // Пользователь близок к концу страницы — подгружаем контент
    fetchMoreContent();
  }
});

Затем реализуем функцию, которая будет запрашивать плейсхолдеры с API и логировать ответ для отладки.

async function fetchMoreContent() {
  try {
    let response = await fetch("https://fakestoreapi.com/products?limit=3");

    if (!response.ok) {
      throw new Error("Сетевой ответ не в порядке");
    }

    let data = await response.json();
    console.log(data);
  } catch (error) {
    console.error("Проблема при получении нового контента:", error);
  } finally {
    console.log("Функция fetch была вызвана");
  }
}

Для этой демо‑страницы мы используем API с fakestoreapi.com.

При прокрутке вы увидите вызовы в консоли:

Подтверждение вызова функции fetch при прокрутке

Предотвращаем множественные параллельные запросы

Если не ограничивать количество одновременных запросов, при быстрой прокрутке браузер отправит много запросов и устройство может замедлиться. Введите флаг состояния загрузки:

let isFetching = false;

И обновите функцию fetchMoreContent, чтобы она игнорировала вызов, если предыдущий всё ещё выполняется:

async function fetchMoreContent() {
  if (isFetching) return; // Выход, если уже происходит загрузка

  isFetching = true; // Устанавливаем флаг

  try {
    let response = await fetch("https://fakestoreapi.com/products?limit=3");

    if (!response.ok) {
      throw new Error("Сетевой ответ не в порядке");
    }

    let data = await response.json();
  } catch (error) {
    console.error("Проблема при получении нового контента:", error);
  } finally {
    console.log("Функция fetch была вызвана");
    isFetching = false; // Сбрасываем флаг
  }
}

Отображение нового контента

Чтобы добавить полученные элементы в DOM, сначала выберите контейнер:

const productsList = document.querySelector(".products__list");

Затем реализуйте функцию вставки:

function displayNewContent(data) {
  data.forEach((item) => {
    const imgElement = document.createElement("img");
    imgElement.src = item.image;
    imgElement.alt = item.title; // Придерживайтесь осмысленных alt для доступности
    productsList.appendChild(imgElement);
  });
}

И обновите fetchMoreContent, чтобы передавать полученные данные в displayNewContent:

async function fetchMoreContent() {
  if (isFetching) return;

  isFetching = true;

  try {
    let response = await fetch("https://fakestoreapi.com/products?limit=3");

    if (!response.ok) {
      throw new Error("Сетевой ответ не в порядке");
    }

    let data = await response.json();
    displayNewContent(data);
  } catch (error) {
    console.error("Проблема при получении нового контента:", error);
  } finally {
    console.log("Функция fetch была вызвана");
    isFetching = false;
  }
}

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

Бесконечная прокрутка работает

Улучшения: индикатор загрузки

Покажите пользователю, что идёт загрузка. Добавьте HTML‑элемент для индикатора:

Загрузка...

Выберите элемент в JS:

const loadingIndicator = document.querySelector(".loading-indicator");

Реализуйте две функции для показа/скрытия индикатора:

function showLoadingIndicator() {
  loadingIndicator.style.display = "block";
  console.log("Загрузка...");
}

function hideLoadingIndicator() {
  loadingIndicator.style.display = "none";
  console.log("Загрузка завершена.");
}

И вызывайте их в fetchMoreContent:

async function fetchMoreContent() {
  if (isFetching) return;

  isFetching = true;
  showLoadingIndicator();

  try {
    let response = await fetch("https://fakestoreapi.com/products?limit=3");

    if (!response.ok) {
      throw new Error("Сетевой ответ не в порядке");
    }

    let data = await response.json();
    displayNewContent(data);
  } catch (error) {
    console.error("Проблема при получении нового контента:", error);
  } finally {
    console.log("Функция fetch была вызвана");
    hideLoadingIndicator();
    isFetching = false;
  }
}

Хорошие практики

  • Не запрашивайте слишком много элементов за один вызов — это может перегрузить память и сеть.
  • Используйте debounce/терминг (задержку) при обработке события scroll, чтобы уменьшить частоту срабатываний.
  • Предоставьте альтернативу: кнопка «Показать ещё» или классическая пагинация для пользователей, которые так предпочитают.
  • Если данных больше нет, явно сообщите об этом пользователю и перестаньте отправлять запросы.
  • Подумайте о lazy loading для изображений () и о кэшировании ответов на клиенте.

Доступность и SEO

  • У каждого изображения должен быть осмысленный alt‑тег.
  • Бесконечная прокрутка может затруднить индексацию и навигацию для пользователей клавиатуры и скринридеров. Для критичного контента предоставьте семантическую пагинацию или aria‑метки, указывающие на состояние загрузки.

Когда бесконечная прокрутка не подходит

  • Если пользователю важно добраться до футера с информацией (контакты, права, ссылки), бесконечная прокрутка может мешать.
  • Для поискового и каталожного контента, где пользователи привыкли к страницам и URL, традиционная пагинация будет удобнее.

Альтернативные подходы

  • «Показать ещё» — сочетает контроль пользователя и ленивую загрузку.
  • Пагинация — улучшает предсказуемость навигации и SEO.
  • Виртуализация списка (windowing) — для больших коллекций экономит память (пример: библиотеки react-window, virtual-scroller).

Быстрый чек‑лист для внедрения

  • Определить порог срабатывания (например, 100px от низа).
  • Ввести флаг isFetching и throttling/debounce на scroll.
  • Добавить индикатор загрузки и сообщения об ошибке.
  • Проверить доступность (alt, aria-live для статуса загрузки).
  • Ограничить размер пакета данных и поддержать lazy loading изображений.

Факт‑бокс

  • Подходит для: ленты новостей, галерей, социальных фидов.
  • Не подходит для: каталогов с важной пагинацией, страниц с критичным SEO.
  • Ключевые приёмы: debounce, флаг загрузки, индикатор состояния, явный конец списка.

Примеры отказа и тесты приёмки

  • Отказ: при очень медленном соединении множественные запросы могут накапливаться — тест: симулировать медленную сеть и убедиться в единственном активном запросе.
  • Критерии приёмки:
    • Новые элементы корректно подгружаются при достижении порога.
    • Индикатор загрузки отображается при запросе и скрывается по завершении.
    • Нет наложения одинаковых запросов при быстрой прокрутке.
    • Скринридер получает уведомление об обновлении контента (aria‑live).

Часто задаваемые вопросы

Что лучше: бесконечная прокрутка или пагинация?

Это зависит от сценария. Для непрерывного потребления контента (ленты, социальные сети) — бесконечная прокрутка удобна. Для каталогов и SEO‑важных страниц лучше пагинация.

Как избежать перегрузки запросами?

Используйте флаг isFetching, throttle/debounce при обработке scroll и ограничьте количество элементов в одном запросе.

Как обеспечить доступность?

Добавьте осмысленные alt‑теги для изображений, aria‑live регион для уведомлений о новой порции контента и альтернативную навигацию (кнопка «Показать ещё»).

Итог

Бесконечная прокрутка — мощный инструмент для улучшения UX при просмотре длинных списков, особенно на мобильных устройствах. Главное — контролировать частоту запросов, показывать пользователю статус загрузки и думать о доступности и альтернативной навигации.

Поделиться: X/Twitter Facebook LinkedIn Telegram
Автор
Редакция

Похожие материалы

Как разделить меш в Blender
3D моделирование

Как разделить меш в Blender

Как увеличить изображение без потери качества
Фото

Как увеличить изображение без потери качества

Как создать влог на iPhone — полное руководство
Видео

Как создать влог на iPhone — полное руководство

Как отразить экран на телевизор — все способы
Руководство

Как отразить экран на телевизор — все способы

Бесконечная прокрутка в Vue 3 — useInfiniteScroll
Vue

Бесконечная прокрутка в Vue 3 — useInfiniteScroll

Чёрный экран iPhone: как восстановить устройство
iPhone

Чёрный экран iPhone: как восстановить устройство