IntersectionObservers сообщают вам, когда наблюдаемый элемент входит или выходит из области просмотра браузера.
Допустим, вы хотите отслеживать, когда элемент в вашем DOM попадает в видимую область просмотра . Возможно, вы захотите сделать это, чтобы иметь возможность вовремя загружать изображения или потому, что вам нужно знать, действительно ли пользователь просматривает определенный рекламный баннер. Вы можете сделать это, подключив событие прокрутки или используя периодический таймер и вызвав getBoundingClientRect()
для этого элемента.
Однако этот подход очень медленный, поскольку каждый вызов getBoundingClientRect()
заставляет браузер перерисовывать всю страницу и приводит к значительным перебоям в работе вашего веб-сайта. Ситуация становится практически невозможной, когда вы знаете, что ваш сайт загружается внутри iframe, и хотите знать, когда пользователь сможет увидеть элемент. Модель единого источника и браузер не позволят вам получить доступ к каким-либо данным с веб-страницы, содержащей iframe. Это распространенная проблема, например, для рекламы, которая часто загружается с помощью iframe.
IntersectionObserver
был разработан для повышения эффективности этого теста видимости, и он доступен во всех современных браузерах. IntersectionObserver
позволяет узнать, когда наблюдаемый элемент входит или выходит из области просмотра браузера.
Как создать 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
прямо сейчас! Расскажите нам, что вы придумали.