Недавно мне пришлось сделать Scrollspy для моего личного веб-портфолио. Я хотел использовать Intersection Observer, так как сейчас он широко поддерживается. К сожалению, это не так просто, и в Интернете не так много говорится о том, как заставить его работать. Итак, я решил написать небольшую статью, чтобы помочь другим, ищущим то же самое.

Scrollspy - это механизм навигации, используемый Bootstrap. Согласно документам,

Прокрутка

Автоматически обновляйте компоненты навигации или группы списков в зависимости от положения прокрутки, чтобы указать, какая ссылка в данный момент активна в области просмотра.

По сути, это часть JavaScript, которая позволяет вашим заголовкам и спискам знать, какой элемент в данный момент находится на экране. Обычно это используется для выделения активного раздела на панели навигации.

Наблюдатель за перекрестком

Раньше эффект достигался с помощью прослушивателя событий прокрутки, который проверял все разделы один за другим при каждой прокрутке, а затем обновлял раздел, который в данный момент находится в окне просмотра, чтобы он стал активным. К счастью, у нас есть новый IntersectionObserver API, который упрощает задачу и делает ее более эффективной.

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

Если вы не знаете об IntersectionObserver API, прочтите эту отличную статью. Ключевое преимущество наблюдателя пересечения заключается в том, что в отличие от прослушивателя событий прокрутки он является асинхронным.

Параметры и опции

IntersectionObserver API принимает следующие параметры:

  1. Корень пересечения: область, которая должна отслеживаться на предмет пересечения с указанными узлами. По умолчанию используется область просмотра, но также может быть установлен узел DOM.
  2. Поле корня: по умолчанию отслеживается весь корень. Но мы можем указать поля вокруг корня, чтобы контролировать большую (или меньшую, если поля отрицательные) область, чем корень.
  3. Порог: значение от 0 до 1, которое указывает, какая часть целевого элемента (элементов) должна находиться в корне пересечения для запуска функции обратного вызова. 0 означает, как только один пиксель входит в область или как только весь узел покидает область. 1 означает, что весь узел должен быть в регионе. Несколько значений могут быть предоставлены в виде массива.
  4. Обратный вызов: функция, вызываемая всякий раз, когда происходит пересечение.

Первый шаг - создать наблюдателя, используя указанные выше параметры. Затем целевые элементы регистрируются наблюдателем пересечения. Если какой-либо из элементов пересекается с указанной областью в пределах пороговых значений, вызывается обратный вызов. Согласно MDN, синтаксис обратного вызова:

let callback = (entries, observer) => {
  entries.forEach(entry => {
    // Each entry describes an intersection change for one observed
    // target element:
    //   entry.boundingClientRect
    //   entry.intersectionRatio
    //   entry.intersectionRect
    //   entry.isIntersecting
    //   entry.rootBounds
    //   entry.target
    //   entry.time
  });
};

entries параметр содержит запись для всех наблюдаемых узлов. Чтобы узнать, какая запись пересекается с корнем, используйте свойство entry.isIntersecting.

Техника

Допустим, у нас есть страница с несколькими разделами, и нам нужно найти тот, который в настоящее время находится в центре внимания. Здесь мы делаем следующее:

  1. Оставьте threshold равным 0 (значение по умолчанию). Это означает, что всякий раз, когда первый пиксель любого раздела входит в область просмотра или последний пиксель любого раздела покидает корень, будет выполняться обратный вызов.
  2. По умолчанию корень пересечения - это вся область просмотра. Мы хотим, чтобы пересечение наблюдалось на горизонтальной линии. Мы можем сделать это, добавив поля к корню и уменьшив наблюдаемую область до горизонтальной линии. Чтобы установить его ровно посередине экрана, установите rootMargin на -50% 0px.
  3. Получите список всех разделов на странице и подключите их к наблюдателю пересечения.
  4. Каждый раз, когда происходит пересечение, выясняйте, какой элемент вызвал пересечение. Для этого используйте свойство entry.isIntersecting.

Код довольно прост:

window.onload = () => {
  const sections = document.getElementsByTagName("section");
  const label = document.getElementById("section-name");
  
  const observer = new IntersectionObserver((entries) => {
    for(const entry of entries)
      if(entry.isIntersecting)
        label.innerHTML = entry.target.innerText;
    },{
    rootMargin: "-50% 0px"
  });
  for (let i = 0; i < sections.length; i++)
   observer.observe(sections[i]);
};

Вы можете увидеть это в действии здесь: http://intersection-scrollspy.surge.sh/