Blog post image

How to create an infinite scrolling list using the intersection observer API and Vue JS

AuthorGenesis Rivera Rios
Date
6/1/2021
Time6 min read

finished project

In this blog post we’ll be seeing how to use the intersection observer API from the browser to create an infinite scrolling list by observing when a specific element is being displayed and making a get request to an external API to show more data to users.

intersection observer diagram

What is the intersection API?

The intersection API allows us to do something with an HTML element once its visible or stops being visible for the user.

This is the simplified version of what it is and what I think you need to know about it for this tutorial, in case you wish to know more about it here’s the link to the MDN docs.

Let's code

Let's create an html document.

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <title>Intersection observer</title>
        <meta name="description" content="">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/css/bootstrap.min.css">
    </head>
    <body>
        <div id="app">
            <h1 class="text-center">Intersection observer example</h1>
        </div>
        <script src="https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"></script>        
    </body>
</html>

In this HTML we’ll be importing Vue JS in the body tag and bootstrap in the head tag.

Now let’s add a script tag which we’ll use to write our javascript. In the script create a new Vue JS instance.

<script>
let vue = new Vue({
  el:'#app',
  data: { 
  },
  methods: {
  },
  mounted: function () {
  }
});
</script>

It’s important for the first div inside our body tag to have the “app” id since Vue JS will use it to create the instance.

Just in case let’s check our network tab to make sure the scripts we added loaded. Scripts loaded

Now let’s go to our mounted lifecycle method and start creating a configuring an object using “IntersectionObserver”

First let’s create an object called options, were going to use it to configure our intersection observer object with what we want to do. This object will contain three properties.

  • root: Used to let the object know who is the parent of the element we’re observing, if left null it will default to the viewport.
  • rootMargin: It’s the margin around the root element. It is configured like a css id or class. For this tutorial it will not be used.
  • threshold: It indicates the percentage of visibility the target element being observed must have in order to execute the call back method passed in the constructor of the intersection observer. For example if we pass the value 0.5 the object will trigger the call back once the element is 50% visible to the user.
	const options = {
		root: null,
		rootMargin: "0px",
		threshold: 0.5
	};

Now let’s create the callback function. Outside our Vue instance we'll write the following function.

function callback (entries) {
    entries.forEach(entry => {
        if(entry.isIntersecting){
        }
    });
}

This function has a parameter called entries which is a list of IntersectionObserverEntry that are used to check if the element is being intercepted by checking the isIntersecting property.

Now back to the mounted method in our Vue JS instance. Let’s finish creating our object by adding the following lines.

const observer = new IntersectionObserver(callback, options);
const observerHtmlElement = document.getElementById('observer');
observer.observe(observerHtmlElement);

That first line is used to instantiate the object using the IntersectionObserver class from our browser API and in the constructor we pass our callback function and the options object.

Then we have to get the actual element we’re observing which isn’t yet in our HTML but we’ll create it after this.

Finally we have to instruct the object to observe said element.

Now let’s create a div in our HTML and assign an id of observer.

    <body>
        <div id="app">
            <h1 class="text-center">Intersection observer example</h1>
            <div id="observer" ></div>
        </div>
        <script src="https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"></script>        
    </body>
</html>

Now that the intersection observer is configured lets create the API call, well be using a public API called instawebtools.

In the methods area of our vue instance le'ts add the following method.

  methods: {
      getPassengerList: function () {
          fetch(`https://api.instantwebtools.net/v1/passenger?page=${this.page}&size=10`) //https://www.instantwebtools.net/fake-rest-api
              .then(res => res.json())
              .then(data => {
                this.passengers.push(...data.data)
                })
      }
  },

Now let’s go to our data object in our Vue JS instance and add the properties we’re using in this get request.

  data: { 
        page: 1,
        passengers:[]
  },

Now let’s add in our callback for the intersection observer the following lines.

function callback (entries) {
    entries.forEach(entry => {
        if(entry.isIntersecting){
            vue.page = vue.page + 1;
            vue.getPassengerList();
        }
    });
}

Every time the callback is executed and the property isIntersecting is true we’ll call the method in our vue instance with the API request and sum one to the page property.

Finally let's show the user data.

<html>
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <title>Intersection observer</title>
        <meta name="description" content="">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/css/bootstrap.min.css">
    </head>
    <body>
        <div id="app">
            <h1 class="text-center">Intersection observer example</h1>
            <div class="px-4 py-5 my-5 text-center">
                <h1  v-for="passenger in passengers"  class="display-5 fw-bold">{{passenger.name}} - Total trips: {{passenger.trips}}</h1>
                <br>
            </div>
            <div id="observer" ></div>
        </div>
        <script src="https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"></script>        
    </body>
</html>

Here's the finished code.

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <title>Intersection observer</title>
        <meta name="description" content="">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/css/bootstrap.min.css">
    </head>
    <body>
        <div id="app">
            <h1 class="text-center">Intersection observer example</h1>
            <div class="px-4 py-5 my-5 text-center">
                <h1  v-for="passenger in passengers"  class="display-5 fw-bold">{{passenger.name}} - Total trips: {{passenger.trips}}</h1>
                <br>
            </div>
            <div id="observer" ></div>
        </div>
        <script src="https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"></script>        
    </body>
</html>
<script>
function callback (entries) {
    entries.forEach(entry => {
        if(entry.isIntersecting){
            vue.page = vue.page + 1;
            vue.getPassengerList();
        }
    });
}
let vue = new Vue({
  el:'#app',
  data: { 
        page: 1,
        passengers:[]
  },
  methods: {
      getPassengerList: function () {
          fetch(`https://api.instantwebtools.net/v1/passenger?page=${this.page}&size=10`) //https://www.instantwebtools.net/fake-rest-api
              .then(res => res.json())
              .then(data => {
                this.passengers.push(...data.data)
                })
      }
  },
  mounted: function () {
      this.getPassengerList();
      const options = {
          root: null,
          rootMargin: "0px",
          threshold: 0.5
      };
      const observer = new IntersectionObserver(callback, options);
      const observerHtmlElement = document.getElementById('observer');
      observer.observe(observerHtmlElement);
  },
});
</script>

I hope you found this post helpful thank you for reading!, I will be exploring other useful Web Browser APIs and writing about them.

Category: javascriptvuejs