Что такое Intersection Observer и для чего он нужен?
Что проверяют
Понимаете ли вы, чем плохо слежение за видимостью через обработчик прокрутки, и знаете ли готовый браузерный механизм. Попутно проверяют представление о том, что происходит в главном потоке во время скролла.
Как отвечать
IntersectionObserver асинхронно сообщает, как целевой элемент пересекается с прямоугольником корня. Корень это либо область видимости окна, либо элемент-предок с прокруткой; rootMargin раздвигает или сжимает этот прямоугольник, threshold задаёт доли пересечения, при переходе через которые срабатывает колбэк.
const io = new IntersectionObserver((entries) => {
for (const e of entries) {
if (!e.isIntersecting) continue;
e.target.src = e.target.dataset.src;
io.unobserve(e.target);
}
}, { rootMargin: '300px', threshold: 0 });Ключ к сильному ответу это сравнение с прежним подходом. Обработчик прокрутки вызывается часто и обычно дёргает getBoundingClientRect, то есть заставляет браузер синхронно пересчитать геометрию прямо во время прокрутки. Наблюдатель считает пересечения вне обработчика и отдаёт готовые числа в entry, включая intersectionRatio.
Типовые применения: ленивая загрузка тяжёлых блоков, бесконечная лента через элемент-маяк в конце списка, запуск анимаций при появлении, учёт фактического показа блока, включение липкого состояния шапки.
Где спотыкаются
Считают, что наблюдатель знает про настоящую видимость. Он геометрический: элемент, накрытый модальным окном или полностью прозрачный, всё равно считается пересекающимся, поэтому мерить им внимание пользователя нельзя.
Забывают, что колбэк срабатывает сразу после observe, ещё до всякой прокрутки. Код, который трактует вызов как «элемент только что появился», выполняет действие в момент подписки, а опираться нужно на флаг isIntersecting.
Не отписываются. Разовое событие требует unobserve, при размонтировании компонента нужен disconnect, иначе наблюдатели копятся и держат ссылки на удалённые узлы. Для картинок часто достаточно нативного loading="lazy", и свой наблюдатель не нужен вовсе.