54

I'm very new to the IntersectionObserver API, and I've been experimenting with this code:

let target = document.querySelector('.lazy-load');

let options = {
    root: null,
    rootMargin: '0px',
    threshold: 0
}

let observer = new IntersectionObserver(callback, options);

observer.observe(target);

function callback() {
    console.log('observer triggered.');
}

This seems to work as it should, and callback() is called whenever .lazy-load element enters the viewport, but callback() also fires once when the page is initially loaded, which triggers `console.log('observer triggered.');

Is there a reason for this callback to be triggered when the page loads? Or is there a mistake in how I'm implementing this?

Edit: Altering the code to the below still fires the callback at page load.

let target = document.querySelector('.lazy-load');

let options = {
    root: null,
    rootMargin: '0px',
    threshold: 0
}

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

        console.log('observer triggered.');

    });
};

let observer = new IntersectionObserver(callback, options);

observer.observe(target);
rpivovar
  • 3,150
  • 13
  • 41
  • 79

2 Answers2

78

That is the default behaviour. When you instantiate an instance of the IntersectionObserver, the callback will be fired.

It is recommended to guard against this case.

entries.forEach(entry => {
  if (entry.intersectionRatio > 0) {
    entry.target.classList.add('in-viewport');
  } else {
    entry.target.classList.remove('in-viewport');
  }
});

Also I found this article as well as the docs to be very helpful, specifically about the intersectionRatio or isIntersecting properties on the IntersectionObserverEntry.

· https://www.smashingmagazine.com/2018/01/deferring-lazy-loading-intersection-observer-api/

· https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver

· https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserverEntry

snewcomer
  • 2,020
  • 1
  • 19
  • 22
  • Awesome answer. Thanks. – rpivovar Nov 20 '18 at 02:18
  • 9
    I think is better to rely on `isIntersecting` because sometimes I've seen some cases where `intersectingRatio` was zero but `isIntersecting` was true ---which in my case was the correct behaviour. – zenw0lf May 26 '20 at 19:31
  • 1
    @zenw0lf Great point! That is what I do here... https://github.com/snewcomer/intersection-observer-admin/blob/28b25180c231b09c61239b6440e8f48a345ea808/src/index.ts#L229 – snewcomer Jun 03 '20 at 02:37
  • Sad that you can't use the case when it's NOT intersecting to .unobserve() the element. Only with some ugly mutators that mark if the element has already been in the viewport – Dr. Freddy Dimethyltryptamine Jun 15 '21 at 14:05
  • ' When you instantiate an instance of the IntersectionObserver, the callback will be fired'. My understanding is that the first time you call the observe method of the new IntersectionObserver then that invokes the callback. If you have a button to just invoke the observe method then that should asynchronously invoke the IntersectionObserver. – JoePythonKing Dec 28 '21 at 09:31
  • Hello, @snewcomer could you take a look at my question? https://stackoverflow.com/questions/75204977/intersectionobserver-to-create-a-lazy-load-images-with-data-srcset-and-imagekit – Sophie Jan 24 '23 at 11:53
2

as easy as it sounds I was able to fix the issue by

  1. adding a threshold comparison condition
  2. adding a slight delay for initialization of observer
    const options = {
      threshold: 1.0,
    };

      setTimeout(() => {
        observer = new IntersectionObserver(([entry]) => {
          console.log("OBSERVER TRIGGERED 1");

          if (
            entry &&
            entry.isIntersecting &&
            entry.intersectionRatio >= options.threshold
          ) {
            console.log("OBSERVER TRIGGERED 2");
          }
        }, options);

        observer.observe(observerRef.value);
      }, 2000);

I would also suggest temporary changing the background color for observable element to something like:

.observer {
  background-color: red;
}

and doing the page refresh. This way your might actually see the red background flashing on your screen hence triggering the event.

Now, before you throw tomatoes at me - in my case - I have a dozen of videos on the webpage. The video HTML elements are not "expanded" right away, because browser needs to download information about the poster images. Hence the page was loaded but vides were still loading one by one.. Adding a slight delay fixed the issue so the browser had time to expand the video contents.

Alex
  • 4,607
  • 9
  • 61
  • 99
  • Hello, @alex could you take a look at my question? https://stackoverflow.com/questions/75204977/intersectionobserver-to-create-a-lazy-load-images-with-data-srcset-and-imagekit – Sophie Jan 24 '23 at 11:53