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

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

Browser Support

  • Хром: 51.
  • Край: 15.
  • Firefox: 55.
  • Сафари: 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 опцию, которая позволяет вам определить альтернативу viewport в качестве корневой. Важно помнить, что 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 намеренно не является ни идеальным по пикселям, ни малозадерживаемым. Использование их для реализации таких начинаний, как зависящие от прокрутки анимации, обречено на провал, поскольку данные будут — строго говоря — устаревшими к тому времени, когда вы сможете их использовать. В Explainer есть более подробная информация об исходных вариантах использования IntersectionObserver .

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

Коротко и ясно: слишком большое время обратного вызова приведет к задержке вашего приложения — применимы все общепринятые методы.

Иди вперед и пересеки свои элементы.

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

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