How to implement Infinite Scrolling with IntersectionObserver API

How to implement Infinite Scrolling with IntersectionObserver API

·

5 min read

Infinite scrolling is one of the many amazing features anyone can build for their projects. It requires no user interaction results in a smooth and beautiful user experience. In this blog, we are going to build a simple vanilla JS app with the help of the IntersectionObserver to implement infinite scrolling like this.

So what is IntersectionObserver?

IntersectionObserver is a javascript API that is used to observe changes in an intersection of an element with an ancestor element if specified, or the browser viewport. It helps us to check a specific element entering or exiting a given viewport and accordingly allows us to write functionality for dynamic interactions, for example, infinite scrolling.

How to use it?

To use IntersectionObserver, we need to declare a variable and create an IntersectionObserver. We are going to name it the lastElementObserver with the purpose check the intersection of the last element to initialise infinite scrolling

let lastElementObserver = new IntersectionObserver(callback , options)

To observe an element we must use the observe method like so:

lastElementObserver.observe(document.quesrySelector("#last-element")) // just an example

Now our observe is observing the last element. Now given the logic of the callback function and the options provided, it will invoke the callback function. Now lets see what are these options and callback.

options

The options is a simple JS object with three properties, root, rootMargin , threshold

root : it is the root element with respect to which we are going check whether the observed element is intersecting or not. If not specified, it will be defaulted to the browser viewport.

rootMargin : It is the margin around the root. It is used to shrink or grow each or any side of the root and given in the form of a string in similar to CSS format like this “10px 15px 20px 25px” (top , right, bottom, left)

threshold : it is a number from 0 to 1, which represents the percentage of the observed element to be visible in order to trigger the callback function. if the callback is to be run when half of the component is visible in the given root, the threshold value is to be set to 0.5. Furthermore, we can give an array as threshold like [0 , 0.3 , 0.5 1] where for each percentage visibililty (0 , 30% , 50% and 100%) the callback is going to be called i.e. all the four times. The default value is 0, which means as soon as a single pixel is visible, the callback function is called.

callback

Now comes the important part of the IntersectionObserver, the callback function. The callback function has 2 parameters, the entries and the observer.

entries: "entries" the array of all the observed elements. The entry in the array has various properties, including the target element, but one property is of main interest, the isIntersecting boolean.

observer: it is our main observer object observing the specific target element. We will mostly use it to unobserve the elements in our project.

So the basic logic we can see is like this :

function callback(entries , callback){
    entries.forEach((entry) => {
        // observer logic
    })
}

Now let’s see how we will use it in our app

Using it for infinite scrolling

The basic html required for the app is here

<h1>Infinite Scroll</h1>
<ul id="display-box"></ul>
<div id="error"></div>

All the pokemons are going to be pushed in the display-box ul element

The js file starts with this:

const displayBox = document.querySelector("#display-box");
const errorDisplay = document.querySelector("#error");
let OFFSET = 0; // store the offset after each subsequent call

The fetching logic is given below. This is a basic API call and this helps us fetch all pokemon serially.

function fetchPokemons() {
  fetch('https://pokeapi.co/api/v2/pokemon?limit=10&offset='+OFFSET)
    .then((res) => res.json())
    .then((data) => {
          renderPokemonsList(data.results); // our logic to display the pokemons in the display box
    })
    .catch((err) => {
      errorDisplay.textContent = err.message;
    });
}

Now here is the fun part. Display all the pokemons and observe the last element.

Here after creating the li elements and appending them in the display box, we are going to take the last of the li elements and observe it using our observer object.

function renderPokemonsList(results) {
  let pokemonArray = results.map((pokemon) => {
    let li = document.createElement("li");
    li.textContent = pokemon.name;
    return li;
  });
  pokemonArray.forEach((pokemonElement) => {
    displayBox.append(pokemonElement);
  });
  lastElementObserver.observe(pokemonArray[pokemonArray.length - 1]);
    // 👆🏻 we are targeting the last element and observing it using our observer
  errorDisplay.textContent = "";
  OFFSET += 10; // to fetch next 10 pokemons in the next call
}

Now for the IntersectionObserver logic,

const lastElementObserver = new IntersectionObserver(
  (entries) => {
    let [lastElement] = entries;
    if (lastElement.isIntersecting) {
      fetchPokemons();
      lastElementObserver.unobserve(lastElement.target);
    }
  },
  {
    threshold: 0.5,
  }
);

In this code, as we know, we are only observing one element at a time, we are going to take it from the array.

Then we are going to check the lastElement.isIntersecting boolean to check whether the element has intersected with our viewport. That means, that we are going to check whether the last element in the list is visible or not.

If it is visible, that means we have reached the end of the list. In that case, we want to call the fetchPokemons function again and render a new set of pokemons.

And that's it! It only took us 40 lines of code to implement this logic and add infinite scrolling. You can further use the same principle and implement any such feature using IntersectionObserver!

To check out the project click here. To check the GitHub link for the project, you can visit my GitHub repo too.

If you have any queries, suggestions or feedback please do let me know. You can also follow me on Twitter and LinkedIn.

So see you folks! Until next time!👋🏻