Короткий ответ: бесконечный скроллинг делается на связке JavaScript и PHP. JavaScript замечает, что пользователь дошел почти до конца страницы, отправляет запрос на PHP-файл, а PHP возвращает следующую порцию записей в JSON
PHP сам по себе не “слушает скролл” в браузере. Скролл происходит на клиенте, в JavaScript. PHP работает на сервере: получает запрос, берет данные из массива или базы и возвращает ответ
Структура проекта
Создадим три файла
infinite-scroll/
index.php
api.php
data.php
В реальном проекте данные обычно берутся из базы. Для учебного примера начнем с массива, чтобы не отвлекаться на SQL
Файл с данными
data.php
<?php
$posts = [];
for ($i = 1; $i <= 100; $i++) {
$posts[] = [
'id' => $i,
'title' => 'Запись ' . $i,
'text' => 'Короткое описание записи номер ' . $i,
];
}
return $posts;
Так мы имитируем 100 записей
PHP endpoint для подгрузки
api.php
<?php
header('Content-Type: application/json; charset=utf-8');
$posts = require __DIR__ . '/data.php';
$limit = 10;
$offset = isset($_GET['offset']) ? (int) $_GET['offset'] : 0;
if ($offset < 0) {
$offset = 0;
}
$chunk = array_slice($posts, $offset, $limit);
echo json_encode([
'items' => $chunk,
'nextOffset' => $offset + count($chunk),
'hasMore' => $offset + count($chunk) < count($posts),
], JSON_UNESCAPED_UNICODE);
Этот файл возвращает не HTML, а JSON. Параметр offset говорит, сколько записей уже загружено
Страница со скроллингом
index.php
<!doctype html>
<html lang="ru">
<head>
<meta charset="utf-8">
<title>Бесконечный скроллинг на PHP</title>
<style>
body { font-family: Arial, sans-serif; max-width: 720px; margin: 40px auto; }
.post { border-bottom: 1px solid #ddd; padding: 20px 0; }
.loader { padding: 24px; text-align: center; color: #666; }
</style>
</head>
<body>
<h1>Бесконечный скроллинг на PHP</h1>
<div id="posts"></div>
<div id="loader" class="loader">Загрузка</div>
<script>
const postsElement = document.getElementById('posts');
const loaderElement = document.getElementById('loader');
let offset = 0;
let loading = false;
let hasMore = true;
function renderPost(post) {
const element = document.createElement('article');
element.className = 'post';
element.innerHTML = `
<h2>${post.title}</h2>
<p>${post.text}</p>
`;
postsElement.appendChild(element);
}
async function loadPosts() {
if (loading || !hasMore) {
return;
}
loading = true;
loaderElement.textContent = 'Загрузка';
const response = await fetch(`api.php?offset=${offset}`);
const data = await response.json();
data.items.forEach(renderPost);
offset = data.nextOffset;
hasMore = data.hasMore;
loading = false;
loaderElement.textContent = hasMore ? 'Прокрутите ниже' : 'Записей больше нет';
}
window.addEventListener('scroll', () => {
const nearBottom = window.innerHeight + window.scrollY >= document.body.offsetHeight - 300;
if (nearBottom) {
loadPosts();
}
});
loadPosts();
</script>
</body>
</html>
Откройте страницу через локальный сервер. Сначала загрузятся первые записи, затем при прокрутке будут подгружаться следующие
Как запустить
В папке проекта выполните
php -S localhost:8000
Откройте
http://localhost:8000
Прокрутите страницу вниз. Если новые записи появляются без перезагрузки, бесконечный скроллинг работает
Как заменить массив на базу данных
В реальном проекте api.php обычно делает SQL-запрос с LIMIT и OFFSET
select id, title, text
from posts
order by id desc
limit 10 offset 20
В PHP значения limit и offset нужно приводить к числу, как в примере выше. Нельзя подставлять пользовательский ввод в SQL как строку без проверки
Почему нужен флаг loading
Без loading браузер может отправить несколько запросов подряд, пока пользователь находится у нижней части страницы. Тогда записи могут продублироваться или прийти не в том порядке
Флаг hasMore нужен, чтобы остановить запросы, когда сервер сказал: записей больше нет
Мини-практика
Измените limit с 10 на 5 и проверьте, как чаще начнутся подгрузки. Затем добавьте кнопку Загрузить еще и вызывайте loadPosts() по клику. Это хороший запасной вариант для пользователей, у которых автоматическая прокрутка работает неудобно
Частые ошибки
Пытаются сделать скролл только на PHP PHP работает на сервере. Событие прокрутки ловит JavaScript в браузере
Не защищают offset Приводите offset к числу и не доверяйте данным из $_GET
Отправляют много запросов подряд Используйте флаг loading, чтобы не запускать новую подгрузку до завершения старой
Не показывают конец списка Пользователь должен понимать, что записей больше нет
Что почитать дальше по PHP
Если нужен общий маршрут по теме, откройте рубрику PHP. Для соседних задач пригодятся эти разборы:
- Composer в PHP: установка пакетов без ручного копирования
- Joomla: как убрать index.php из адреса сайта
- Laravel: первый route и controller для новичка
- Linux install PHP: установка PHP на Ubuntu и проверка окружения



