Руководство по Intersection Observer
Современные веб-страницы активно используют событие скролла для обнаружения элементов, видимых в границах текущей области просмотра viewport
. Скроллинг может запускать отложенную загрузку изображений и данных, анимацию элементов, поддерживать бесконечную загрузку содержимого и многое другое.
Intersection Observer API
предоставляет возможность наблюдать за заданными элементами и упрощает реализацию отслеживания изменения в их пересечении с заданным элементом-предком или самим окном просмотра viewport
, то есть слежения за видимостью элемента.
Концепция Intersection Observer
Рассмотрим несколько ключевых терминов, использующихся для определения любого экземпляра Intersection Observer
:
Observer (наблюдатель)
результат вызова классаIntersectionObserver
, его экземпляр, объект с методамиRoot (корень)
это элемент, который ожидает пересечения элемента-цели. По умолчанию, это окно просмотра браузераviewport
, но может использоваться любой другой элемент, если этого требует задачаTarget (цель)
элемент, за которым следит наблюдатель и оповещает о его вхождении в корень. Целью может быть любой элемент. Один наблюдатель может отслеживать множество различных целей
При расчете пересечений, Intersection Observer
рассматривает все элементы как прямоугольник, вне зависимости от их внешнего вида. Эти прямоугольники рассчитываются как наименьшие из возможных, при этом они содержат все целевое содержимое:
Можно увеличить или уменьшить размеры границ корня для расчёта пересечений. Для этого используется rootMargin
. При этом геометрия корня не меняется, изменения касаются только расчетов границ пересечения:
Экземпляр класса
Для использования, достаточно создать новый экземпляр класса IntersectionObserver
, который принимает два аргумента — callback-функцию и объект настроек.
IntersectionObserver(callback, options)
Callback-функция
По умолчанию, коллбэк будет вызываться, когда элемент появляется и исчезает из области видимости. Первым аргументом коллбэк получит массив объектов IntersectionObserverEntry
, каждый из которых содержит набор свойств элемента, показываемого в области видимости:
boundingClientRect
intersectionRatio
intersectionRect
rootBounds
target
time
Проще всего их распаковать вот таким образом:
const {
time,
rootBounds,
boundingClientRect,
intersectionRect,
target,
isIntersecting,
isVisible,
intersectionRatio
} = entry;
// в итоге мы получим доступ к каждому свойству в константе с его названием
// например добавляем CSS-класс к наблюдаемому элементу через target.classList.add( 'hello' );
target ()
Благодаря этому свойству мы получаем доступ к отслеживаемому элементу и можем производить с ним какие-либо действия, например добавить или удалить CSS-класс.
const { target } = entry;
target.classList.add( 'hello' );
isIntersecting ()
Этот параметр равен true
, когда наблюдаемый элемент хотя бы на 1 пиксель пересекает наблюдаемую область. Единственное, о чём важно не забывать — это о параметре threshold
, который влияет на то, когда будет срабатывать колбэк-функция.
В качестве примера давайте попробуем добавлять CSS-класс на элемент, когда он попадает в область видимости, и удалять, когда он из неё выходит:
// callback-функция (возвратная функция)
const trueCallback = function (entries, observer) {
entries.forEach((entry) => {
// получаем свойства, которые доступны в объекте entry
const {target, isIntersecting} = entry;
if (isIntersecting) {
// добавляем класс, когда элемент входит в область наблюдения
target.classList.add('some-class');
} else {
// удаляем класс, когда элемент из неё выходит
target.classList.remove('some-class');
}
});
}
Это свойство обычно используется в качестве дополнительного условия внутри callback-функции. И оно зависит от параметра threshold
.
Суть этого свойства в том, что оно содержит информацию о том, на сколько процентов наблюдаемый элемент пересекает наблюдаемую область. Хотя сама эта информация не в процентах, а в значениях от 0 до 1. То есть например значение intersectionRatio
равное 0.25
будет означать, что элемент пересекает область на 25%
.
Вторым аргументом в коллбек передается ссылка на сам экземпляр обзервера.
Объект настроек
Объект настроек позволяет задать опции с которыми будет создан обзервер. Можно изменить root
, который по умолчанию равен viewport
, задать отступы от границ контекста rootMargin
Порог вхождения threshold
в виде массива значений. Порог означает, сколько процентов элемента должно попасть в область видимости для срабатывания коллбэка. Значения могут варьироваться от 0.01
до 1.0
.
root
Правильнее всего этот параметр можно описать, как область наблюдения. По умолчанию это viewport
, видимая часть страницы сайта в окне браузера, но это может быть какой-то определённый HTML-элемент на странице.
rootMargin
В этот параметр мы можем передать значения, которыми можно увеличить или уменьшить область root-элемента. Значения передаются точно в таком же формате, в котором они передаются при работе с обычным CSS-свойством margin
. То есть вы можете передать какое-то одно значение, которое будет применяться со всех сторон, например 25px
, либо передать несколько значений в одну строку, для каждой стороны '10px 11% -10px 25px
(в таком порядке — сверху справа внизу слева).
threshold
В первое время этот параметр может сбивать с толку, поэтому, чтобы понять его предназначение лучше, взгляните на данные, значение threshold
колеблится от 0
до 1
:
0
в этом случае возвратная функция сработает два раза — как только первый пиксель элемента попадёт в область наблюдения, и как только последний пиксель покинет область наблюдения0.5
в данном случае возвратная функция сработает, когда центр элемента 50% будет пересекать область наблюдения в любом направлении[0, 0.2, 0.5, 1]
мы можем передать массив значений. И для каждого из них будет срабатывать возвратная функция. В такой ситуация возвратная функция срабатывает при:- первый пиксель элемента попадает в область наблюдения, либо последний пиксель выходит из области наблюдения
- 20% элемента внутри области наблюдения (напоминаю, что направление не имеет значения)
- то же самое для 50%
- когда элемент полностью в области наблюдения
Практический пример Intersection Observer
Экземпляр класса, это наблюдатель или другими словами объект с набором методов, с помощью которых можно следить за DOM-элементами:
const options = {
rootMargin: '50px',
threshold: 0.5,
};
const onEntry = (entries, observer) => {
entries.forEach(entry => {
// тут можно писать логику для проверки вхождения
});
};
const observer = new IntersectionObserver(onEntry, options);
После того, как экземпляр IntersectionObserver
создан, необходимо предоставить один или несколько целевых элементов для наблюдения. Для этого на экземпляре вызывается метод observe(elem)
, который принимает ссылку на DOM-элемент за которым необходимо наблюдать. Когда элемент войдет или выйдет в область видимости root
, будет вызвана callback-функция, переданная при создании экземпляра.
const options = {
rootMargin: '50px',
threshold: 0.5,
};
const onEntry = (entries, observer) => {
entries.forEach(entry => {
// тут можно писать логику для проверки вхождения
});
};
const observer = new IntersectionObserver(onEntry, options);
observer.observe(elem);
Рабочий пример из реального проекта:
// параметры
const options = {
// строка закомментирована, чтобы использовать значение по умолчанию
// root: document.querySelector( '#viewport' ),
rootMargin: '0px',
threshold: [ 0, 0.5 ]
};
// обьект
const observer = new IntersectionObserver( trueCallback, options );
// находим элемент
const target = document.querySelector( '#target' );
// запускаем "слежку" за элементом(ами) в константе target
observer.observe( target );
// callback-функция
const trueCallback = function(entries, observer) {
// получаем данные элемента
entries.forEach((entry) => {
// делаем что-либо для каждого переданного элемента, например можно добавить какой-либо CSS-класс элементу
entry.target.classList.add( 'some-class' );
// прекращаем наблюдение
observer.unobserve(entry.target);
});
}