Stefan Bajić
Stefan Bajić

Reputation: 497

IntersectionObserver unobserve not working on target in callback?

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

Answers (3)

Kaiido
Kaiido

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

Andreas
Andreas

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

David Bradshaw
David Bradshaw

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

Related Questions