Перевод словосочетания Intersection Observer - это наблюдатель за пересечением. То есть Intersection Observer API - это интерфейс, который позволяет отследить с помощью JavaScript, как какой-либо объект входит в область просмотра.

Если вы разрабатываете сайт с динамическими эффектами при прокрутке — анимацией, подгрузкой контента или подсветкой активных разделов — вам точно стоит познакомиться с IntersectionObserver. Этот встроенный  в JavaScript API позволяет отслеживать, какие элементы видны в области просмотра, без необходимости писать громоздкие обработчики scroll/onscroll.

Для чего нужен IntersectionObserver?

IntersectionObserver позволяет:

  • определить, входит ли элемент в видимую часть экрана (viewport);
  • узнать насколько он виден — частично или полностью;
  • выполнить действие, как только элемент появился в зоне видимости;
  • оптимизировать производительность, не отслеживая каждый scroll вручную.

Где применять IntersectionObserver

Давайте рассмотрим, каким образом мы можем использовать Intersection Observer API для решения различных задач.

Создание экземпляра объекта Intersection Observer

Для начала необходимо создать экземпляр IntersectionObserver (например, с именем observer):

Здесь callback(entries, observer) - это функция, которая вызывается каждый раз, когда наблюдаемые элементы пересекают порог видимости.

Первый параметр в функции - entries — массив IntersectionObserverEntry, каждый из которых содержит:

  • entry.target — DOM-элемент;
  • entry.isIntersecting — true, если элемент пересекает область видимости;
  • entry.intersectionRatio — число от 0 до 1: процент видимости элемента;
  • entry.boundingClientRect — позиция и размер элемента;
  • и др.

Затем надо определиться с тем, какой элемент для вас интересен в плане наблюдения за ним, и после этого связать  и вызвать observer и наблюдаемый элементом с помощью метода observe():

При этом InterSectionObserverEntry и объекты-наблюдатели будут регистрироваться в консоли каждый раз, когда наблюдаемый элемент пересекает или выходит из области просмотра. Сама запись содержит свойства с данными о цели, boundingClientRect и correctionRatio, среди прочего.

Простой пример

Ниже вы увидите пример, в котором наблюдаются в каждом блоке есть изображение и текст. Класс fancy с анимацией добавляется или удаляется в зависимости от того, находится ли изображение в области просмотра или нет.

Сам пример:

See the Pen Animated images with IntersectionObserver by HTML-plus (@ambassador) on CodePen.

Обработка пересечений для нескольких элементов

В большинстве случаев, как и в приведенном примере, вам нужно будет наблюдать сразу несколько элементов. Это можно сделать с помощью того же наблюдателя пересечения (IntersectionObserver) и его параметра entries:

Мы еще не определяем, находятся ли наблюдаемые элементы в поле зрения или вне поля зрения, просто сработало пересечение. Вот как вы можете воздействовать на наблюдаемые элементы, входя в представление или покидая его:

Мы просто проверяем, не превышает ли значение intersectionRatio изображения0, чтобы узнать, вошло ли оно в область просмотра.

Что такое intersectionRatio?

Это доля (от 0 до 1), которая показывает, какая часть элемента находится в зоне наблюдения (viewport или root), относительно полной высоты или ширины элемента.

Примеры:

  • intersectionRatio = 0 → элемент полностью вне экрана
  • intersectionRatio = 0.550% элемента видны
  • intersectionRatio = 1 → элемент полностью в зоне видимости

Почему это важно:

Если в настройках вы задаёте threshold: 0.5, это значит, что intersectionRatio должен стать ≥ 0.5, чтобы entry.isIntersecting стал true.

Ниже вы найдете демо-пример с визуальной индикацией intersectionRatio — в правом верхнем углу в реальном времени отображается значение от 0 до 1, а блок меняет цвет в зависимости от того, насколько он виден.

See the Pen Untitled by HTML-plus (@ambassador)  on CodePen.

Что такое boundingClientRect?

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

Структура boundingClientRect:

Зачем нужен boundingClientRect?

  • Понять, где именно находится элемент на экране.
  • Узнать размеры элемента.
  • Рассчитать динамически, виден ли элемент полностью или частично.
  • Полезно для анимации, lazy loading, фиксации положения и других эффектов.

Это свойство - как "замер линейкой" положения и размера элемента на экране. Иными словами,  оно показывает, где находится элемент по отношению к окну браузера (viewport) и каких он размеров.

Представим: у вас есть прямоугольный блок, и он сейчас частично виден на экране. Тогда entry.boundingClientRect может вернуть, например:

Наглядно:
┌───────────── ВЕРХ ОКНА (viewport top = 0px)

│ ← top: 150px

│ [ блок ← height: 200px│ в экране ]
│ ← bottom: 350px

└───────────── НИЗ ОКНА

Такое отслеживание может понадобиться для:

  • Проверки, вошёл ли элемент в экран и насколько.
  • Сделать анимацию, когда top < 100, например.
  • Отследить момент, когда элемент почти вышел за верх экрана.

Например, мы можем вывести в консоль:

Практический пример: запуск анимации при достижении определенной точки экрана

Мы уже говорили, что наиболее частой проблемой, которую решают с помощью Intersection Observer, является добавление или удаление анимации при скролле страницы. Здесь как раз можно использовать свойство entry.boundingClientRect

See the Pen Box Animation with body scroll by HTML-plus (@ambassador) on CodePen.

И в этом примере есть один интересный момент, который нужно учитывать при использовании Intersection Observer: если для блоков с классом box задать свойство transform: translateX(400%) или transform: translateX(-400%) , то вы НЕ увидите анимации появления при скролле страницы!

Дело в том, что такое значение свойства смещает элемент на 400% его собственной ширины вправо или влево. То есть: если .box шириной 400px, то400% = 1600px вправо. А это ширина многих экранов, если не больше. Такой элемент физически выходит за пределы области viewport (видимой зоны), и IntersectionObserver не видит его, потому что:

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

Вот такие пироги, малята :))
А вот свойствоtransform:translateX(100%) — это всего лишь на одну ширину элемента (например, 400px). Он всё ещё рядом с экраном, и как только ты чуть-чуть прокрутишь — он начинает "пересекать" viewport, и IntersectionObserver срабатывает. Поэтому в анимации у нас стоят по соседству 2 свойства: transform:translateX(100%) и opacity: 0 для начального положения .box.

В принципе, эту проблему можно решить  и другим способом, используя обработку события onscroll и метод getBoundingClientRect().

See the Pen Untitled by Elen (@ambassador) on CodePen.

Если обобщить:

  • IntersectionObserver работает с фактической позицией элемента в viewport, а не с тем, что видно визуально после transform.Поэтому большие значения translateX/Y/Z могут «спрятать» элемент от наблюдения.
  • Легкие анимации с translateX(100%) + opacity: 0 — отличный компромисс, который хорошо работает и для UX, и для SEO/производительности.

Настройки (options), или конфигурация для IntersectionObserver

Для IntersectionObserver можно сразу же при создании добавить настройки, которые являются вторым необязательным параметром при создании экземпляра класса. Их еще называют конфигурацией, или  по-англмийски options, например:

Здесь можно использовать такие параметры и значения:

Параметр Значение по умолчанию Описание
root null (viewport) Область, в которой проверяется видимость (можно указать контейнер). Это элемент, используемый для проверки пересечения. По умолчанию используется document, но вы можете изменить значение по умолчанию, например, для чего-то вроде iframe.
rootMargin '0px' Отступы от области root, задаются в пикселях или % как у CSS margin. Полезно для "раннего срабатывания". Например: '3rem 2rem'
threshold 0 Порог срабатывания: от 0 (пересечён хотя бы 1 пиксель) до 1 (виден полностью). Можно указать массив: [0, 0.5, 1]. Это массив числовых значений от 0 до 1. Значения соответствуют соотношению видимости элемента, где 0 полностью вне поля зрения, а 1 полностью находится в поле зрения. Если вы укажете несколько значений, обратный вызов пересечения будет вызываться при достижении каждого заданного порогового значения.

Еще один пример конфигурации и ее использования:

Давайте еще раз поподробнее о rootMargin: это настройка объекта IntersectionObserver, которая расширяет или сужает зону "видимости" (viewport или другого root) по каждому краю.
Формат такой же, как у CSS-свойства margin - от 1 до 4 возможных значений в таком порядке: top right bottom left.

Например, в значении "-100px 0px 0px 0px":

  • -100pxверхний отступ (top): уменьшает зону видимости сверху на 100 пикселей.
    Элемент считается "видимым" только когда он опустится на 100px ниже верха экрана.
  • 0pxправый отступ: не меняется.
  • 0pxнижний отступ: не меняется.
  • 0pxлевый отступ: не меняется

Проще говоря: с rootMargin: "-100px 0px 0px 0px" элемент начнёт считаться видимым не сразу при входе в viewport, а только когда он будет ниже верхней границы экрана на 100 пикселей.

┌──────────── viewport top
│ ← этот участок (-100px) теперь НЕ считается частью видимости

│ ▼ элемент должен попасть сюда, чтобы считаться "видимым"
├──────────── 0px (новый порог)


└──────────── viewport bottom

Практический пример: изменение цвета и фона в заголовке сайта

Например, у нас есть сайт с темным разделом в самом верху и прозрачным хэдером с белым текстом для пунктов меню. Однако, все остальные разделы - белые или светлые. Поэтому после пересечения со вторым разделом хэдер должен получить плотный светлый фон и пункты меню темного, а не светлого цвета. С цветами нам помогут разобраться CSS-переменные, а вот отслеживать, когда закончится первый раздел поручим InterSectionObserver:

See the Pen Intersection Observer for Header background by HTML-plus (@ambassador) on CodePen.

По сути дела, все, что нам надо сделать - это добавить или удалить класс nav-scrolled для хэдер: header.classList.add("nav-scrolled");

Пример: увеличение заголовка при попадании в зону видимости

Допустим, у нас есть несколько секций с заголовками. Мы хотим, чтобы заголовок активной секции увеличивался, а остальные оставались небольшими. В этом примере изменения в размере шрифта вступают в силу при появлении 60% раздела (section) на экране.

See the Pen onScroll re-sizing Header (observer) by HTML-plus (@ambassador) on CodePen.

Здесь:

  • Используется IntersectionObserver, чтобы отслеживать, какая секция находится в поле зрения (более 60%).
  • Заголовку этой секции добавляется класс .active — он масштабирует шрифт.
  • У всех остальных заголовков этот класс убирается, и они возвращаются к 30px.

Реализуем lazy loading с IntersectionObserver

Вероятно, наиболее очевидный вариант использования Intersection Observer API - это отложенная загрузка (lazy loading) изображений, когда они отображаются после того, как появляются в поле зрения. Использование Intersection Observer делает его менее ресурсоемким и намного более простым в реализации по сравнению с прослушиванием событий прокрутки и проверкой того, собирается ли элемент войти в область просмотра. За кулисами Intersection Observer API использует requestIdleCallback, чтобы еще больше повысить производительность.

Идея реализации lazy loading проста:

  • В HTML у картинок используется не src, а data-src, чтобы изначально изображения не было видно.
  • Когда изображение попадает в viewport (или рядом с ним) — IntersectionObserver заменяет data-src на src, и браузер начинает загрузку картинки.

Пример ленивой загрузки изображений с IntersectionObserver

See the Pen Пример lazy load изображений by HTML-plus (@ambassador) on CodePen.

Что происходит в примере при использовании IntersectionObserver для lazy loading?

  • в HTML-разметке data-src используется вместо src, чтобы браузер не начал загружать изображение сразу.
  • В настройках обсервера rootMargin: '100px' позволяет начинать загрузку немного заранее, до того как изображение попадет точно в viewport.
  • observer.unobserve(img) отключает наблюдение, чтобы не выполнять повторные действия после загрузки.

Усложним пример, добавив тег <picture>и скелетон на месте еще не загруженной картинки. Принцип действия прост - прокрутка  вниз → картинка подгружается → плавно появляется → скелетон исчезает.

See the Pen Lazy Load &lt;picture&gt;+ Skeleton by HTML-plus (@ambassador) on CodePen.

Отключаем наблюдение с помощью Observer.unobserve()

Метод observer.unobserve(target) прекращает отслеживание пересечения указанного DOM-элемента target с областью просмотра (viewport) или root-контейнером.

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

Просто говоря: это "отписка" от наблюдения за элементом, который больше не интересен.

Здесь, например, когда элемент входит в нужную область, выводим сообщение в консоли, а затем мы прекращаем наблюдение за этим элементом, чтобы сообщение больше не регистрировалось:

Если один раз произошло нужное событие (например, изображение подгрузилось), то нет смысла продолжать наблюдение. Это:

  • экономит производительность,
  • предотвращает повторные вызовы callback,
  • логически завершает "миссию" Observer'а.

На практике метод unobserve() применяют, когда элемент стал видимым, и мы выполнили нужное действие. То есть необходимость в наблюдении за элементом отпала. Фактически unobserve() нужен там, где анимация появления срабатывает один раз.

В примере ниже, который уже попадался нам, реализованы 2 варианта Observer'a: один для заголовка без использования метода unobserve(), т.к. действие должно срабатывать многократно, а второй - с ним, т.к. анимация появления картинок может быть отображена всего один раз.

See the Pen Intersection Observer for Header &amp; img by HTML-plus (@ambassador) on CodePen.

А в этом примере Observer следит за появлением списка задач с анимацией и отключается, когда список прокручен полностью. Сами задачи при клике становятся перечеркнутыми (выполненными) и, кроме того, сохраняются в localStorage. Отметьте пару-тройку задач, как выполненные, и нажмите кнопку Rerun в правом нижнем углу.  Вы увидите, что отмеченные задачи имеют перечеркнутый текст.

See the Pen Tasks and observer.disconnect() by HTML-plus (@ambassador) on CodePen.

Метод Observer.disconnect()

Вы также можете в любой момент вызвать Observer.disconnect(), чтобы полностью прекратить наблюдение за всеми наблюдаемыми элементами. Это значит, что вы можете одним махом отписаться от всех наблюдаемых элементов сразу, написав строку observer.disconnect().

В примере ниже у нас будет эмуляция просмотра контента, который меняет фон при попадании в область просмотра:

  • Каждая секция "подсвечивается" при попадании в viewport.
  • Коллекция Set собирает data-id уникальных просмотренных секций.
  • Как только все просмотрены, метод disconnect() отключает наблюдение.
  • Показывается уведомление "Все секции просмотрены ✅".

See the Pen observer.disconnect() by HTML-plus (@ambassador) on CodePen.

Ну, и теперь давайте посмотрим еще ряд практических примеров использования IntersectionObserver API.

Практические примеры с использованием IntersectionObserver API

Всплытие блока при прокрутке

Цель: При появлении элемента на экране — он плавно "всплывает" (с fade и сдвигом вверх).

See the Pen RevealBlocks with IntersectionObserver by HTML-plus (@ambassador) on CodePen.

Что происходит:

  • .reveal изначально прозрачный и немного смещён вниз;
  • Как только элемент попадает хотя бы на 30% в область видимости, ему добавляется класс .visible;
  • CSS-анимация делает его плавно прозрачным и поднимает вверх.

Вы можете также использовать rootMargin: '0px 0px -20% 0px', если хотите, чтобы эффект срабатывал заранее.

Всплытие отзывов при попадании в зону видимости/использование data-атрибутов

В этом примере для задания кастомных настроек мы будем использовать data-атрибуты:

  • data-direction - направление появления: top, bottom, left, right
  • data-delay - задержка появления, напр. 0.3s

See the Pen Testimonials with IntersectionObserver by HTML-plus (@ambassador) on CodePen.

See the Pen Scroll-Snap (Intersection Observer) by HTML-plus (@ambassador) on CodePen.

Пример прокрутки контента

Пример прокрутки контента от автора Arden, в котором Intersection Observer API отслеживает положение скролла.

See the Pen Intersection Observer API fun [Don't use in production] by Arden (@aderaaij) on CodePen.

Пример смены разделов страницы при скроллинге

Автор Ryan Mulligan предлагает использовать Intersection Observer и css-свойство clip-path для смены разделов страницы.

See the Pen Animating Clip-Path Sections w/ Intersection Observer by Ryan Mulligan (@hexagoncircle)on CodePen.

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

Контент страницы здесь формируется на основе JS-кода с помощью Fetch API и async/await.  А при прокрутке страницы IntersectionObserver отслеживает совпадение id заголовков и ссылок на них в содержании.  Автор Lisi.

See the Pen Table of Contents - 4 - Scroll To by Lisi (@lisilinhart) on CodePen.

"Проявление" изображений при появлении их в области просмотра.

See the Pen Open Props - Bento Grid (Container Queries, text-wrap: pretty) by Arby (@mobalti) on CodePen.

Галерея изображений, сменяющихся при прокрутке

See the Pen MidJourney Style &lt;img&gt; scroller with CSS by Jhey (@jh3y) on CodePen.

Заключение

Если вы работаете с лендингами, SPA или интерфейсами, где важна реакция на прокрутку — IntersectionObserver станет незаменимым инструментом. Он даёт контроль, удобство и производительность без необходимости "слушать" scroll 60 раз в секунду.

Преимущества IntersectionObserver

  • Производительность: не нагружает scroll-событие;
  • Простота кода: декларативная логика, меньше ручного расчёта;
  • Адаптивность: работает во всех современных браузерах;
  • Умный UX: можно показать или активировать элемент точно в нужный момент.

Поддержка браузеров

IntersectionObserver поддерживается во всех современных браузерах (проверьте на  caniuse.com). Даже в IE11 его можно использовать через полифилл от W3C.

Автор: Админ

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *