9

I am using the Intersection Observer API and I was wondering:

How can I debounce or throttle the Intersection Observer API?

If I want to increase the performance, do you recommend to use a debounce or throttle function?

Alfrex92
  • 6,278
  • 9
  • 31
  • 51

2 Answers2

4

Observer in general (not only intersection observer, but also mutation observer, ...) are all handled by the browser. Meaning the browser decides when the Observer should be executed.

That's why you only define thresholds and depending on the load, they are more or less accurate. For example, when you define a threshold for 20% on the Intersection Observer, it may be executed at 20.6%. That's because the browser debounces (delay might be the better word here) the function automatically and only executes it once it has enough resources to execute it.

This is also true for all other observers.

cloned
  • 6,346
  • 4
  • 26
  • 38
4

I'd like to start this by saying that the Intersection Observer API is very performant already and provides several configurations to make the callbacks happen more or less often depending on the need, avoiding the necessity of debouncing or throttling its calls. If you want to learn more, give a quick read to this article I put together about the Intersection Observer API.

Having that said, we'd need to understand how the threshold property of the configuration works. The threshold goes from any number between 0 and 1, and it accepts either a number or an Array of numbers. So all of the following options are valid:

const sneakPeekOptions = {threshold: 0};
const fullOptions = {threshold: 1};
const progressiveOptions = {threshold: [0, .25, .5, .75, 1]};

To better understand the threshold, the values can be multiplied by 100 and the result will tell us the visibility percentage of the target that will trigger the callback. This would mean:

// When 0% is visible
const sneakPeekOptions = {threshold: 0};

// When 100% is visible
const fullOptions = {threshold: 1};

// When 0%, 25%, 50%, 75% and 100% is visible
const progressiveOptions = {threshold: [0, .25, .5, .75, 1]};

So for the sneakPeekOptions and the fullOptions is evident that this could only happen once as the user scrolls, so let's focus on the progressiveOptions one.

Now we have to add another variable which is the height (or width? it depends on the scrolling direction, so for now let's focus on the height and vertical scrolling) of the element being observed. Let's propose two node elements:

<head>
  .
  .
  .
  <style>
    .small-target { height: 4px }
    .large-target { height: 100px }
  </style>
</head>
<body>
  . // More elements creating scroll
  .
  .
  <div class="small-target></div>
  <div class="large-target></div>
  .
  .
  . // More elements creating scroll
</body>

These two elements .small-target and .large-target have 4px and 100px height respectively. So now let's execute the following JS lines:

const smallTarget = document.querySelector('.small-target');
const largeTarget = document.querySelector('.large-target');

const progressiveOptions = {threshold: [0, .25, .5, .75, 1]};
const callback = (e, o) => {}; // To be called on each threshold.

const observer = new IntersectionObserver(callback, progressiveOptions);

observer.observe(smallTarget);
observer.observe(largeTarget);

As seen above, we're configuring an observer with an Array of thresholds to observe two elements with different heights. Now, considering the user is scrolling at 1px per second (let's imagine this is possible) the frequency of the calls to the callback function will drastically change depending on the height of the element:

Calls for smallTarget

In this case the callback function will get called 1 time per second, meaning it would have a total of 5 calls after 4 seconds:

  1. One call at 0px – 0 seconds – start
  2. One call at 1px – 1 second – scrolling
  3. One call at 2px – 2 seconds – scrolling
  4. One call at 3px – 3 seconds – scrolling
  5. One call at 4px – 4 seconds – end

Calls for largeTarget

Now, in this case the callback function will get called 1 time every 25 seconds meaning it would have a total of 5 calls after 100 seconds:

  1. One call at 0px – 0 seconds – start
  2. One call at 25px – 25 seconds – scrolling
  3. One call at 50px – 50 seconds – scrolling
  4. One call at 75px – 75 seconds – scrolling
  5. One call at 100px – 100 seconds – end

I think this is a great way of picturing how the same observer can perform calls more or less frequent depending of the height of the component being observed.

The recommendation

If you're seeing that the IntersectionObserver is getting called too often try to play around with the threshold configuration and the height of the observed element, I can assure you there's a way to make this calls less frequent either by changing the thresholds or by using a separate Intersection Observer instance.

Even though I consider this should cover every scenario you may face, if you still need to debounce and/or throttle after tweaking the thresholds I'd love to know more about this scenario and provide a better answer if possible.

wilsotobianco
  • 1,360
  • 14
  • 19