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

Я пытаюсь сделать так, чтобы некоторые текстовые элементы перестали перекрывать темный фон, они будут индивидуально менять цвет один за другим по мере прокрутки пользователем. Все текстовые элементы position: fixed

РЕДАКТИРОВАТЬ: MDN документы говорят (выделение мое):

API Intersection Observer предоставляет способ асинхронно наблюдать изменения в пересечении целевого элемента с элементом- предком

Я думаю, что это означает, что нет никакой возможности решить мою проблему, потому что элементы, которые я хочу контролировать для перекрытия, не являются потомками root Я указываю в опциях объекта.

Есть ли способ обнаружить перекрытие, если перекрывающийся элемент не является дочерним по отношению к другому элементу?

if ('IntersectionObserver' in window) {

    const options = {
        root: document.getElementById('flow-landing'),
        rootMargin: '0px',
        threshold: 0
      }


    var callback = function(entries, observer) { 
            entries.forEach(entry => {

                if (entry.isIntersecting) {
                    entry.target.style.color = "white";
                }
                else {
                    entry.target.style.color = null;
                }
            });
          };

    const observer = new IntersectionObserver(callback, options);

    var targets = [Array.from(document.querySelectorAll('.social-item')), Array.from(document.querySelectorAll('.additional-item'))].flat();

    targets.forEach(target => 
        observer.observe(target));
}

Нет никаких ошибок консоли, но код ничего не делает.

3 ответа

Немного изменил ответ Руслана, потому что в его ответе создается несколько объектов Intersection Observer.

Можно наблюдать за несколькими элементами с помощью одного и того же наблюдателя, вызвав .observe() на нескольких элементах.

      let observerOptions = {
    rootMargin: '0px',
    threshold: 0.5
}

var observer = new IntersectionObserver(observerCallback, observerOptions);

function observerCallback(entries, observer) {
    entries.forEach(entry => {
        if(entry.isIntersecting) {
          //do something
        }
    });
};

let target = '.targetSelector';
document.querySelectorAll(target).forEach((i) => {
    if (i) {
        observer.observe(i);
    }
});

Вы можете сделать что-то подобное, по крайней мере, мне это помогает:

document.querySelectorAll('.social-item').forEach((i) => {
    if (i) {
        const observer = new IntersectionObserver((entries) => {
            observerCallback(entries, observer, i)
        },
        {threshold: 1});    
        observer.observe(i);
    }
})

const observerCallback = (entries, observer, header) => {
    entries.forEach((entry, i) => {
         if (entry.isIntersecting) {
             entry.target.style.color = "white";
         }
         else {
             entry.target.style.color = null;
         }
    });
};

Вы могли бы использовать offsetTop а также offsetHeight свойства вместо IntersectionObserver API.

Например, когда пользователь прокручивает, вы можете проверить, offsetTop из element 2 больше, чем offsetTop и меньше чем offsetHeight из element 1,

ВНИМАНИЕ: используйте debounce, потому что функция обработчика события прокрутки будет вызываться один раз для каждого пикселя, который пользователь прокручивает, просто представьте себе кошмар производительности, который случится, если пользователь прокрутит 600-1000 пикселей.

Документация по LoDash описывает функцию debounce следующим образом:

"[функция, которая] создает отклоненную функцию, которая задерживает вызов func (обработчик) до wait (время) миллисекунды прошло с тех пор, как в последний раз вызываемая функция была вызвана ".

Если вы не используете LoDash, вот функция отладки Vanilla JavaScript:

function debounce(handler, time) {
  var timeout;
  return function() {
    var self = this, args = arguments;
    clearTimeout(timeout);
    timeout = setTimeout(function() {
      return handler.apply(self, args);
    }, time);
  };
}

Вот код, который позволит вам "делать вещи", если element 2 "пересекается" element 1,

let element_1 = document.querySelector("#my-element-1");
let element_2 = document.querySelector("#my-element-2");
window.addEventListener("scroll", debounce(() => {
  if(element_2.offsetTop > element_1.offsetTop && element_2.offsetTop < element_1.offsetHeight) {
    console.log("The elements are intersecting.");
  }
}, 100));

В случае, если это выглядит сложным или трудным для чтения, вот тот же код, разбитый на более мелкие куски:

let element_1 = document.querySelector("#my-element-1");

let element_2 = document.querySelector("#my-element-2");

window.addEventListener("scroll", debounce(() => {

  let x = element_2.offsetTop > element_1.offsetTop;

  let y = element_2.offsetTop < element_1.offsetHeight;

  if(x && y) {

    console.log("The elements are intersecting.");

  }

}, 250));

Примечания и информация:

  1. Вы могли бы использовать >= а также <= операторы вместо > а также < операторы
  2. wait слишком долгое время может заставить эффект выглядеть неестественным и вынужденным.

Удачи.

Другие вопросы по тегам