
В предыдущей статье мы ознакомились с основами использования Intersection Observer API. Теперь давайте остановимся подробнее на некоторых его опциях для настройки «наблюдателя». Мы также рассмотрим два разных подхода для наблюдения сразу за несколькими элементами.
Опции и настройки Intersection Observer API
Интерфейс Intersection Observer API предоставляет возможность применять несколько опций и настроек. Например, имеется возможность передачи массива параметров (все они необязательны) в качестве второго аргумента конструктора new IntersectionObserver():
- root — родительский элемент для наблюдения за целевыми элементами, расположенными внутри него. По умолчанию — это область просмотра, но им также может быть и любой другой прокручиваемый элемент.
- rootMargin — маргины, используемые для корневого элемента при обнаружении пересечения. Этот параметр работает аналогично свойству margin в CSS, и по умолчанию имеет значение 0px 0px 0px 0px. Установка положительного числа приведет к тому, что наблюдаемые элементы будут иметь значение isIntersecting, равное true, прежде чем они появятся в области видимости (или другом родительском элементе — root).
- threshold — параметр Intersection Observer API, отвечающий за то, какая часть элемента должна находиться в области видимости (или в root элементе), чтобы он считаться видимым. Значение 0 (установлено по умолчанию) означает, что учитывается даже один пиксель, значение 1 — что все пиксели элемента должны быть видны. Для срабатывания метода при появлении элемента на 25% достаточно установить пороговое значение в 0,25. Можно передать одно число или массив чисел.
Например, представим, что нам нужно отложить загрузку текста в определенные блоки, когда они находятся на расстоянии 50 пикселей от границы входа в область видимости.
Для удобства чтения давайте также перенесем функцию обратного вызова в ее собственную именованную функцию.
// Ленивая загрузка текста function loadText (entries, obs) { entries.forEach(function (entry) { // Если наблюдаемый элемент находится за пределами зоны видимости, то ничего не происходит if (!entry.isIntersecting) return; // Отключаем «наблюдателя» obs.unobserve(entry.target); // Выводим в консоль сообщение, когда элемент войдет в зону видимости console.log('Элемент в зоне видимости'); // Добавляем в него текст entry.target.textContent += 'Элемент в зоне видимости'; }); } // Задаем опции для «наблюдателя» let options = { rootMargin: '50px' }; // Создаем новый «наблюдатель» let observer = new IntersectionObserver(loadText, options); // Указываем элемент для «наблюдения» let app = document.querySelector('.element'); // Прикрепляем «наблюдателя» к элементу observer.observe(app);
Наблюдение за несколькими элементами
Intersection Observer API позволяет обеспечивать наблюдение сразу за несколькими элементами с запуском одной и той же callback-функции для каждого из них.
При наблюдении за несколькими элементами сразу массив entries может (но не обязательно) содержать несколько элементов.
Если element_1 и element_2 не расположены рядом друг с другом, один может покинуть область просмотра, когда второй еще в нее не вошел. В этой ситуации тот, который из нее выйдет, сохраняется в массиве entries со значением isIntersecting равным false. А второй будет иметь значение true.
// Создаем новый «наблюдатель» let observer = new IntersectionObserver(function (entries) { console.log(entries); entries.forEach(function (entry) { console.log(entry.target); console.log(entry.isIntersecting); }); }); // Указываем элементы для «наблюдения» let el_1 = document.querySelector('.element_1'); let el_2 = document.querySelector('.element_2'); // Прикрепляем элементы к «наблюдателя» observer.observe(el_1); observer.observe(el_2);
При этом в entries отображаются только элементы с измененным состоянием. Если бы они не находились рядом друг с другом на странице и только один элемент пересекался с границей области видимости, то только он находился в entries в callback-функции.
Альтернативный шаблон — один наблюдатель на элемент
Подключение нескольких элементов к одному Intersection Observer API может привести к некоторым неудобствам. Это в первую очередь актуально при командной работе над одним проектом.
В качестве альтернативы можно использовать один «наблюдатель» для каждого элемента. При таком подходе полезно использовать именованную функцию обратного вызова и внешнюю переменную для параметров.
Еще одним способом является создание вспомогательной функции, которая создает новый конструктор IntersectionObserver(). Она активирует «наблюдение» за элементом и вернет «наблюдателя» (observer).
При использовании этого подхода entries всегда будет содержать только один элемент. Для простоты можно применить деструктуризацию и присвоить первый элемент переменной.
/** Создаем intersection observer elem - элемент для «наблюдения» callback - callback-функция options - объект опций, если они необходимы (необязательный параметр) */ function createIntersectionObserver (elem, callback, options) { let observer = new IntersectionObserver(callback, options || {}); observer.observe(elem); return observer; } /** Выводим в консоль элемент, если он входит в зону видимости entries - массив «наблюдаемых» элементов */ function log (entries) { let [entry] = entries; console.log(entries); console.log(entry.target); console.log(entry.isIntersecting); } // Задаем опции для «наблюдателя» let options = { rootMargin: '50px' }; // Указываем элементы для «наблюдения» let el_1 = document.querySelector('.element_1'); let el_2 = document.querySelector('.element_2'); /** Создаем «наблюдателя» для каждого элемента Для первого указываем опции */ createIntersectionObserver(el_1, log, options); // Второй использует ту же самую callback-функцию, но без опций createIntersectionObserver(el_2, log);
В свое время этот способ применения Intersection Observer API вызвал многочисленные споры. Однако в результате большинство ведущих разработчиков пришли к следующему выводу.
Использование большого количества «наблюдателей» с одним элементом и одного «наблюдателя» с несколькими элементами имеет примерно одинаковую эффективность. Поэтому каждый может выбрать для себя тот, который ему нравится больше. Особой разницы в них нет.
Кроссбраузерность
Учитывая, что Internet Explorer не поддерживает Intersection Observer API, а в Safari и FireFox Android имеются некоторые проблемы, без полифила не обойтись.