Reputation: 497
I'm trying to add elements to the page as the user scrolls down. When the last element comes into view fully I want the observer callback to be called, a new element created, the observer set up to observe the new element and to no longer observe the current target.
function observerCallback(entries, observer) {
entries.forEach((entry) => {
const newDiv = document.createElement('div')
observer.observe(newDiv)
document.body.appendChild(newDiv)
observer.unobserve(entry.target);
});
}
However this doesn't work as I expect it to and the callback just keeps getting called and new elements are spawned. I've set up a codepen to demonstrate the issue.
Why does this happen?
Upvotes: 0
Views: 6522
Reputation: 137014
It's simply that calling IntersectionObserver.observe(target)
will fire the callback for that target
.
const observer = new IntersectionObserver(entries => {
console.log( 'new entry!', entries[0].target );
});
// wait a bit to show that it's really the call to observe() that does trigger the callback
setTimeout(()=> {
// even if not attached
const elem = document.createElement('div');
observer.observe( elem );
}, 2000);
So unobserve
works fine, it's just that the new observed element will itself trigger a new callback call.
Not knowing exactly what you are trying to do it's hard to tell you how to fix your code, but that should help you get to the correct route, maybe you are just looking for the isIntersecting
property, so it stops when the last element is not in view anymore.
let observer = new IntersectionObserver(observerCallback, { threshold: 1.0 });
function observerCallback(entries, observer) {
entries.forEach((entry) => {
if( !entry.isIntersecting ) {
return;
}
const newDiv = document.createElement('div')
observer.observe(newDiv)
document.body.appendChild(newDiv)
observer.unobserve(entry.target);
});
}
observer.observe(document.querySelector('div'))
div {
height: 20px;
background-color: red;
margin-bottom: 5px;
}
<div></div>
Upvotes: 4
Reputation: 21911
You have to check if the entry
is visible (intersecting with the container)
if (entry.isIntersecting) {
//...
}
and only then (or if the threshold is above a certain value) add a new element to the DOM.
let observer = new IntersectionObserver(observerCallback, { threshold: 1.0 });
function observerCallback(entries, observer) {
entries.forEach((entry) => {
if (!entry.isIntersecting) {
return;
}
const newDiv = document.createElement('div')
observer.observe(newDiv)
document.body.appendChild(newDiv)
observer.unobserve(entry.target);
});
}
observer.observe(document.querySelector('div'))
div { height: 50px; background-color: red; margin-bottom: 20px }
<div></div>
Upvotes: 1
Reputation: 13087
Is it just the order you are doing things in your function? CodePen doesn’t work on my iPad for some reason, so I can not test this just now.
function observerCallback(entries, observer) {
entries.forEach((entry) => {
const newDiv = document.createElement('div')
observer.unobserve(entry.target)
document.body.appendChild(newDiv)
observer.observe(newDiv)
});
}
Upvotes: 0