IntersectionObserver's coming into view

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

Browser Support

  • Chrome: 51.
  • Edge: 15.
  • Firefox: 55.
  • Safari: 12.1.

Source

Допустим, вы хотите отслеживать, когда элемент в DOM попадает в видимую область просмотра. Это может быть полезно, например, если вы хотите настроить отложенную загрузку изображений или узнать, действительно ли пользователь видит определенный рекламный баннер. Для этого можно подключить событие прокрутки или использовать периодический таймер и вызвать getBoundingClientRect() для этого элемента.

Однако этот подход очень медленный, поскольку каждый вызов getBoundingClientRect() заставляет браузер перекомпоновывать всю страницу и приводит к значительным задержкам на сайте. Если же сайт загружается в iframe, то определить, когда пользователь видит элемент, практически невозможно. Модель единого источника и браузер не позволят вам получить доступ к данным на веб-странице, содержащей iframe. Это распространенная проблема для объявлений, которые часто загружаются с помощью iframe.

Чтобы сделать эту проверку видимости более эффективной, был разработан IntersectionObserver, который теперь поддерживается во всех современных браузерах. IntersectionObserver позволяет узнать, когда отслеживаемый элемент появляется в области просмотра браузера или исчезает из нее.

Видимость iframe

Как создать IntersectionObserver

API довольно небольшой, и его лучше всего описать на примере:

const io = new IntersectionObserver(entries => {
  console.log(entries);
}, {
  /* Using default options. Details below */
});

// Start observing an element
io.observe(element);

// Stop observing an element
// io.unobserve(element);

// Disable entire IntersectionObserver
// io.disconnect();

Если вы используете настройки по умолчанию для IntersectionObserver, обратный вызов будет выполняться как при частичном появлении элемента на экране, так и при его полном исчезновении.

Если вам нужно отслеживать несколько элементов, вы можете и должны делать это с помощью одного и того же экземпляра IntersectionObserver, вызывая observe() несколько раз.

В функцию обратного вызова передается параметр entries, который представляет собой массив объектов IntersectionObserverEntry. Каждый такой объект содержит обновленные данные о пересечении для одного из наблюдаемых элементов.

🔽[IntersectionObserverEntry]
    time: 3893.92
    🔽rootBounds: ClientRect
        bottom: 920
        height: 1024
        left: 0
        right: 1024
        top: 0
        width: 920
    🔽boundingClientRect: ClientRect
    // ...
    🔽intersectionRect: ClientRect
    // ...
    intersectionRatio: 0.54
    🔽target: div#observee
    // ...

rootBounds – результат вызова getBoundingClientRect() для корневого элемента, который по умолчанию является областью просмотра. boundingClientRect – результат вызова getBoundingClientRect() для наблюдаемого элемента. intersectionRect – это пересечение двух прямоугольников, которое показывает, какая часть отслеживаемого элемента видна. intersectionRatio – это тесно связанное свойство, которое показывает, какая часть элемента видна. Используя эту информацию, вы можете реализовать такие функции, как своевременная загрузка объектов до того, как они станут видны на экране. Эффективно.

Коэффициент пересечения.

IntersectionObserver передают данные асинхронно, а код обратного вызова выполняется в основном потоке. Кроме того, в спецификации указано, что в реализациях IntersectionObserver следует использовать requestIdleCallback(). Это означает, что вызов обратного вызова имеет низкий приоритет и будет выполнен браузером во время простоя. Это осознанное дизайнерское решение.

Прокручиваемые элементы div

Мне не очень нравится прокрутка внутри элемента, но я не собираюсь никого судить, как и IntersectionObserver. Объект options принимает параметр root, который позволяет определить альтернативу области просмотра в качестве корневого элемента. Важно помнить, что root должен быть предком всех наблюдаемых элементов.

Пересекайте все, что можно!

Нет! Плохой разработчик! Это нерациональное использование ресурсов процессора. Рассмотрим пример с бесконечной прокруткой. В этом случае определенно рекомендуется добавить в DOM сторожевые элементы и отслеживать (и повторно использовать) их. Добавьте контрольный элемент рядом с последним элементом в бесконечной прокрутке. Когда этот элемент появляется на экране, вы можете использовать обратный вызов, чтобы загрузить данные, создать следующие элементы, добавить их в DOM и соответствующим образом изменить положение контрольного элемента. Если вы правильно утилизируете сигнальный объект, дополнительный вызов observe() не требуется. IntersectionObserver продолжает работать.

Бесконечная прокрутка

Больше обновлений

Как упоминалось выше, обратный вызов будет активирован один раз, когда наблюдаемый элемент частично появится в области просмотра, и ещё раз, когда он покинет ее. Таким образом, IntersectionObserver дает ответ на вопрос "Виден ли элемент X?". Однако в некоторых случаях этого может быть недостаточно.

Для этого и нужен вариант threshold. Оно позволяет задать массив пороговых значений intersectionRatio. Обратный вызов будет выполняться каждый раз, когда intersectionRatio пересекает одно из этих значений. Значение по умолчанию для threshold – [0], что объясняет режим работы по умолчанию. Если мы изменим threshold на [0, 0.25, 0.5, 0.75, 1], то будем получать уведомления каждый раз, когда в области просмотра будет появляться дополнительная четверть элемента:

Анимация порогового значения.

Есть ли другие варианты?

В настоящее время существует только один дополнительный вариант, помимо перечисленных выше. rootMargin позволяет указать поля для корневого элемента, что позволяет увеличить или уменьшить область, используемую для пересечений. Поля задаются с помощью строки в стиле CSS, например "10px 20px 30px 40px", где указываются верхнее, правое, нижнее и левое поля соответственно. Подводя итог, структура параметров IntersectionObserver предлагает следующие варианты:

new IntersectionObserver(entries => {/* … */}, {
  // The root to use for intersection.
  // If not provided, use the top-level document's viewport.
  root: null,
  // Same as margin, can be 1, 2, 3 or 4 components, possibly negative lengths.
  // If an explicit root element is specified, components may be percentages of the
  // root element size.  If no explicit root element is specified, using a
  // percentage is an error.
  rootMargin: "0px",
  // Threshold(s) at which to trigger callback, specified as a ratio, or list of
  // ratios, of (visible area / total area) of the observed element (hence all
  // entries must be in the range [0, 1]).  Callback will be invoked when the
  // visible ratio of the observed element crosses a threshold in the list.
  threshold: [0],
});

<iframe> волшебный

IntersectionObserver были разработаны специально для рекламных сервисов и виджетов социальных сетей, которые часто используют элементы <iframe> и могут извлечь пользу из информации о том, находятся ли они в поле зрения. Если <iframe> наблюдает за одним из своих элементов, то прокрутка как <iframe>, так и окна, содержащего <iframe>, будет запускать обратный вызов в соответствующее время. Однако в последнем случае для rootBounds будет задано значение null, чтобы избежать утечки данных между источниками.

О чем не говорится в IntersectionObserver?

Обратите внимание, что IntersectionObserver намеренно не обеспечивает идеальное качество изображения и низкую задержку. Их использование для реализации таких задач, как анимация, зависящая от прокрутки, обречено на неудачу, поскольку данные будут, строго говоря, устаревшими к тому времени, когда вы их будете использовать. Подробную информацию о первоначальных примерах использования IntersectionObserver можно найти в пояснении.

Какой объем работы можно выполнить в обратном вызове?

Если обратный вызов будет выполняться слишком долго, приложение начнет зависать.

Пересекайте элементы

IntersectionObserver поддерживается всеми современными браузерами. При необходимости в устаревших браузерах можно использовать полифил, доступный в репозитории WICG. Очевидно, что вы не получите преимуществ в производительности, используя этот полифилл, которые дала бы вам нативная реализация.

Вы можете начать использовать IntersectionObserver прямо сейчас. Расскажите, что у вас получилось.