Что такое IntersectionObserver и для чего он используется?

MiddleJavaScript · Frontend·Обновлено 16 августа 2026
Коротко
IntersectionObserver — это браузерный API, который асинхронно отслеживает пересечение элемента с viewport или заданным контейнером, позволяя эффективно реализовывать ленивую загрузку, бесконечную прокрутку и аналитику видимости без обработчиков scroll.

Что это такое

IntersectionObserver — это нативный браузерный API, который позволяет асинхронно отслеживать изменение видимости целевого элемента относительно области просмотра (viewport) или указанного родительского контейнера (root). В отличие от подписки на событие scroll с последующим вызовом getBoundingClientRect(), наблюдатель работает вне основного потока рендеринга и не вызывает принудительный reflow, поэтому он гораздо производительнее.

Как создать наблюдателя

Наблюдатель создаётся конструктором IntersectionObserver, который принимает callback-функцию и объект настроек. Callback вызывается браузером каждый раз, когда меняется степень пересечения хотя бы одного из отслеживаемых элементов с корневым контейнером.

const observer = new IntersectionObserver((entries) => {
  entries.forEach((entry) => {
    if (entry.isIntersecting) {
      console.log('Элемент попал во viewport');
    }
  });
});

const target = document.querySelector('.card');
observer.observe(target);

Основные параметры

  • root — элемент-контейнер, относительно которого считается пересечение. Если null, используется viewport браузера.
  • rootMargin — отступы вокруг root в формате CSS-margin (например '200px 0px'), позволяют «расширить» зону срабатывания заранее, до фактического появления элемента.
  • threshold — число или массив чисел от 0 до 1, задающих долю видимости элемента, при которой должен сработать callback. threshold: 0.5 означает срабатывание при видимости половины элемента.

Каждая запись в entries (IntersectionObserverEntry) содержит isIntersecting, intersectionRatio, boundingClientRect, target и rootBounds.

Практическое применение

Наиболее частые сценарии использования:

  • Ленивая загрузка изображений и iframe вместо атрибута loading="lazy", когда нужен более гибкий контроль.
  • Бесконечная прокрутка (infinite scroll) — подгрузка следующей страницы данных при появлении элемента-триггера в конце списка.
  • Аналитика видимости баннеров и рекламных блоков (viewability tracking).
  • Анимации появления элементов при скролле (reveal on scroll).
  • Управление тяжёлыми виджетами (видео, карты) — пауза при выходе из viewport для экономии ресурсов.

Производительность и очистка

Главное преимущество перед связкой scroll + getBoundingClientRect в том, что вычисления пересечения браузер выполняет асинхронно и оптимизированно, не блокируя основной поток и не провоцируя layout thrashing. После того как элемент больше не нужно отслеживать, важно вызывать observer.unobserve(target) или observer.disconnect(), чтобы не удерживать ссылки на DOM-узлы и избежать утечек памяти.

Что хочет услышать интервьюер

Кандидат объясняет, что IntersectionObserver асинхронный и не блокирует основной поток, в отличие от scroll-обработчиков

Знание ключевых опций: root, rootMargin, threshold и их влияния на срабатывание callback

Понимание структуры IntersectionObserverEntry (isIntersecting, intersectionRatio, boundingClientRect)

Приводит реальные кейсы: ленивая загрузка, infinite scroll, viewability-аналитика

Упоминает необходимость отписки через unobserve/disconnect для предотвращения утечек памяти

Пример: Ленивая загрузка изображений

const images = document.querySelectorAll('img[data-src]');

const lazyObserver = new IntersectionObserver((entries, observer) => {
  entries.forEach((entry) => {
    if (entry.isIntersecting) {
      const img = entry.target;
      img.src = img.dataset.src;
      img.removeAttribute('data-src');
      // после загрузки картинку больше не нужно отслеживать
      observer.unobserve(img);
    }
  });
}, {
  rootMargin: '200px 0px', // начинаем грузить заранее, до входа в viewport
});

images.forEach((img) => lazyObserver.observe(img));

Пример: Бесконечная прокрутка списка

const sentinel = document.querySelector('#load-more-trigger');

const infiniteScrollObserver = new IntersectionObserver(async (entries) => {
  const [entry] = entries;
  if (entry.isIntersecting) {
    // элемент-триггер показался — подгружаем следующую страницу
    await loadNextPage();
  }
}, {
  threshold: 1, // триггер должен быть виден полностью
});

infiniteScrollObserver.observe(sentinel);

Типичные ошибки

Путают IntersectionObserver с ResizeObserver или MutationObserver, приписывая ему не те задачи

Забывают, что threshold: 1 означает полную видимость элемента, а не единичное срабатывание

Не вызывают unobserve/disconnect после того, как элемент больше не нужно отслеживать, что приводит к лишним вычислениям и утечкам

Считают, что callback вызывается синхронно сразу после observe(), хотя первое срабатывание тоже асинхронно

Пытаются использовать один наблюдатель с разными наборами threshold для разных целей вместо создания нескольких наблюдателей

Лучшие курсы по теме

изображение курса

TypeScript с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Feature-Sliced Design

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.6
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Next.js - с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее