Back

Обнаружение попадания элементов в область просмотра с помощью Intersection Observer

Обнаружение попадания элементов в область просмотра с помощью Intersection Observer

Отслеживание видимости элементов с помощью обработчиков событий прокрутки может серьезно снизить производительность вашего веб-сайта. Каждая прокрутка вызывает множественные события, каждое из которых вызывает getBoundingClientRect() и заставляет браузер выполнять дорогостоящие пересчеты макета. API Intersection Observer элегантно решает эту проблему, предоставляя нативную оптимизацию браузера для обнаружения момента, когда элементы входят в область просмотра или покидают её.

Ключевые выводы

  • Intersection Observer устраняет узкие места производительности, вызванные обработчиками событий прокрутки
  • API работает асинхронно, предотвращая блокировку основного потока
  • Один наблюдатель может эффективно отслеживать множество элементов
  • Нативная оптимизация браузера обеспечивает лучшую производительность, чем ручные вычисления

Почему традиционные события прокрутки не справляются

Обработчики событий прокрутки срабатывают непрерывно во время прокрутки, часто вызываясь более 60 раз в секунду. Каждый обработчик событий, который вызывает getBoundingClientRect(), заставляет браузер пересчитывать макеты, создавая прерывистую прокрутку. Когда несколько библиотек независимо отслеживают видимость — для рекламы, аналитики и ленивой загрузки — влияние на производительность многократно усиливается.

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

Основы Intersection Observer

API Intersection Observer асинхронно отслеживает пересечение целевых элементов с границами корневого элемента (обычно области просмотра). Вместо постоянного опроса он уведомляет вас только при пересечении пороговых значений видимости.

const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      console.log('Element is visible');
    }
  });
});

observer.observe(document.querySelector('.target'));

Браузер обрабатывает все вычисления пересечений внутренне, предоставляя результаты через колбэк с объектами IntersectionObserverEntry. Каждая запись предоставляет isIntersecting (булево значение) и intersectionRatio (процент видимости от 0 до 1).

Создание вашего первого наблюдателя

Настройка Intersection Observer требует только функцию колбэка и опциональную конфигурацию:

const callback = (entries, observer) => {
  entries.forEach(entry => {
    // Доступные ключевые свойства
    console.log({
      isVisible: entry.isIntersecting,
      visibilityRatio: entry.intersectionRatio,
      targetElement: entry.target
    });
  });
};

const options = {
  root: null,        // область просмотра
  rootMargin: '0px', // без смещения
  threshold: 0.5     // 50% видимости
};

const observer = new IntersectionObserver(callback, options);
observer.observe(document.querySelector('.target'));

Колбэк получает массив записей, поскольку наблюдатели могут одновременно отслеживать множество элементов. Свойство isIntersecting каждой записи указывает текущую видимость, а intersectionRatio предоставляет точный процент видимости.

Настройка опций наблюдателя

Три опции контролируют, когда срабатывают колбэки пересечения:

root: Определяет прокручиваемую область для наблюдения. null использует область просмотра; любой прокручиваемый элемент может работать как пользовательский корень.

rootMargin: Расширяет или сужает ограничивающий прямоугольник корня. Используйте синтаксис CSS margin: "50px" или "10% 0px". Отрицательные значения сужают; положительные значения расширяют область обнаружения.

threshold: Процент(ы) видимости, запускающие колбэки. Одно значение: 0.5 (50%). Массив для множественных триггеров: [0, 0.25, 0.5, 0.75, 1].

Эффективное наблюдение за множеством элементов

Один экземпляр наблюдателя может отслеживать неограниченное количество элементов:

const observer = new IntersectionObserver(callback, options);
const targets = document.querySelectorAll('.lazy-load');

targets.forEach(target => observer.observe(target));

// Прекратить наблюдение за конкретными элементами
// observer.unobserve(element);

// Прекратить наблюдение за всеми элементами
// observer.disconnect();

Этот паттерн максимизирует эффективность — браузер лучше оптимизирует один наблюдатель, отслеживающий сотни элементов, чем множество наблюдателей.

Примеры реальной реализации

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

const imageObserver = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      const img = entry.target;
      img.src = img.dataset.src;
      imageObserver.unobserve(img);
    }
  });
}, { rootMargin: '50px' });

document.querySelectorAll('img[data-src]').forEach(img => {
  imageObserver.observe(img);
});

Запуск анимаций при прокрутке

const animationObserver = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      entry.target.classList.add('animate');
    }
  });
}, { threshold: 0.1 });

document.querySelectorAll('.animate-on-scroll').forEach(element => {
  animationObserver.observe(element);
});

Автовоспроизведение видео в поле зрения

const videoObserver = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    const video = entry.target;
    if (entry.isIntersecting) {
      video.play();
    } else {
      video.pause();
    }
  });
}, { threshold: 0.5 });

document.querySelectorAll('video').forEach(video => {
  videoObserver.observe(video);
});

Заключение

API Intersection Observer превращает обнаружение попадания в область просмотра из узкого места производительности в оптимизированную функцию браузера. Заменив обработчики событий прокрутки на intersection observers, вы устраняете блокировку основного потока, получая при этом более точный контроль видимости. Начните миграцию вашего кода, основанного на прокрутке, уже сегодня — браузеры ваших пользователей будут вам благодарны.

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

Да, просто вызовите observer.observe() для новых элементов после их добавления в DOM. Один и тот же экземпляр наблюдателя может отслеживать элементы, добавленные в любое время жизненного цикла страницы.

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

Проверьте, что entry.isIntersecting равно false в вашем колбэке. Наблюдатель уведомляет вас как при входе элементов в наблюдаемую область, так и при выходе из неё, основываясь на ваших пороговых настройках.

Современные браузеры поддерживают его нативно. Для старых браузеров, таких как Internet Explorer, используйте официальный полифилл от W3C, который обеспечивает идентичную функциональность через JavaScript.

Understand every bug

Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.

OpenReplay