Один и тот же наблюдатель пересечения для нескольких элементов 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));
Примечания и информация:
- Вы могли бы использовать
>=
а также<=
операторы вместо>
а также<
операторы -
wait
слишком долгое время может заставить эффект выглядеть неестественным и вынужденным.
Удачи.