Как на JS реализовать «бесконечный скролл» (infinite scroll) для подгрузки новостной ленты?

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

## Основные подходы

Существует два популярных способа реализации:
1. **Отслеживание события scroll** — устаревший, менее производительный метод.
2. **IntersectionObserver API** — современный, рекомендуемый подход.

## Реализация через IntersectionObserver

### HTML-разметка
html

`sentinel` — это невидимый элемент-«маяк» в конце списка. Когда он попадает в область видимости, запускается подгрузка.

### JavaScript
javascript
let page = 1;
let isLoading = false;
const feed = document.getElementById(‘news-feed’);
const sentinel = document.getElementById(‘sentinel’);
const loader = document.getElementById(‘loader’);

async function loadNews(page) {
isLoading = true;
loader.style.display = ‘block’;

try {
const response = await fetch(`/api/news?page=${page}&limit=10`);
const data = await response.json();

data.items.forEach(item => {
const article = document.createElement(‘article’);
article.innerHTML = `

${item.title}

${item.summary}

`;
feed.appendChild(article);
});

if (!data.hasMore) {
observer.disconnect(); // Останавливаем наблюдение, если новостей больше нет
}
} catch (error) {
console.error(‘Ошибка загрузки:’, error);
} finally {
isLoading = false;
loader.style.display = ‘none’;
}
}

const observer = new IntersectionObserver((entries) => {
const entry = entries[0];
if (entry.isIntersecting && !isLoading) {
loadNews(page++);
}
}, {
rootMargin: ‘200px’ // Начинаем загрузку за 200px до конца страницы
});

observer.observe(sentinel);
loadNews(page++); // Первоначальная загрузка

## Важные детали реализации

— **Флаг isLoading** предотвращает дублирующие запросы, если пользователь быстро скроллит.
— **rootMargin** позволяет начать загрузку заранее, до того как пользователь доберётся до конца — это убирает видимые задержки.
— **observer.disconnect()** останавливает наблюдение, когда данные закончились.
— **Обработка ошибок** в блоке `try/catch` обязательна — сеть может быть нестабильной.

## Дополнительные улучшения

— Добавьте **скелетон-загрузку** вместо простого текста «Загрузка…» для лучшего UX.
— Реализуйте **дебаунс** или **throttle** при использовании метода через scroll-событие.
— Сохраняйте позицию скролла в `sessionStorage`, чтобы при возврате на страницу пользователь не терял место.
— Используйте **виртуализацию DOM** (например, библиотеку `virtual-scroller`) при очень длинных лентах, чтобы не перегружать браузер тысячами DOM-узлов.

Такая реализация работает в всех современных браузерах и не требует сторонних библиотек.


Задайте вопрос нейросети

Не нашли ответ? Спросите ИИ — он подготовит развёрнутую статью.