Blog post image

Infinite scrolling utilizando el intersection observer API y Vue JS

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

finished project

En este blog post estaremos viendo cómo podemos utilizar el intersection observer API para crear el efecto de una lista infinita, cuando el usuario de nuestra aplicación comience a desplazarse hacia abajo utilizando el cursor del buscador web estaremos utilizando el observer API para “observar” cuando llegamos a un cierto element de HTML el cual estará a lo último de nuestro viewport estaremos haciendo un get request a un api externo el cual utilizaremos para presentar nuevos datos al usuario.

intersection observer diagram

¿Qué es el intersection observer API?

El intersection API nos permite observar si un elemento especifico esta visible o no para el usuario y de acuerdo a eso tomar alguna acción.

Esto es una versión simplificada de lo que es, entiendo que es todo lo que necesitas saber para este tutorial pero en el caso de que desees conocer más sobre este API aquí dejo el enlace.

Comencemos a programar

<!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>

En este documento de HTML vamos a importar Vue JS en el script tag adentro del body y bootstrap en el head.

Ahora vamos a crear un script tag abajo de nuestro HTML y en el vamos a crear una nueva instancia de Vue JS.

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

Es importante que ese primer div adentro del body tag en nuestro HTML tenga el id de “app” ya que Vue JS lo utilizara al momento de crear la instancia.

Solo por si acaso, vamos a verificar el Network tab en nuestro buscador web para asegurarnos que los scripts fueron cargados. Scripts loaded

Ahora vamos a ir al mounted de nuestra instancia de Vue y comenzar a crear y configurar un objeto del “IntersectionObserver.

Primero vamos a crear un objeto llamado options, se estará utilizando para configurar el intersection observer con lo que querramos hacer. Este objeto debe tener tres propiedades.

  • root: se utiliza para dejarle saber al intersection observer quien es el padre del element que estará observando. Si esta propiedad se deja vacia se utilizara el viewport por defecto.
  • rootMargin: Es el margen alrededor del root (propiedad que se explico anteriormente). Se configura como si fuera una propiedad en una clase o id de CSS. Para efectos de este tutorial no la estaremos utilizando.
  • threshold: Indica el porcentaje de visibilidad que debe tener el elemento al cual estamos antes de ejecutar nuestro método (acción que tomaremos). Por ejemplo si escribimos el valor 0.5 entonces nuestro método se ejecutara cada vez que el elemento este a 50% de visibilidad.
	const options = {
		root: null,
		rootMargin: "0px",
		threshold: 0.5
	};

Ahora vamos a escribir el callback function. Afuera de nuestro Vue instance escribiremos la siguiente función.

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

Esta funcion recibe una lista llamada entries la cual se utiliza verificar si nuestro elemento es visible, con la propiedad isIntersecting podemos asegurarnos de que el elemento es visible al momento de la ejecución del callback.

Ahora vamos a volver al mounted de Vue JS. Continuaremos creando nuestro objeto utilizando las siguientes líneas de javascript.

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

Esa primera línea se utiliza para instanciar el objeto utilizando la clase IntersectionObserver de nuestro browser API y en el constructor le vamos a pasar nuestro callback function y el objeto de las opciones.

La siguiente línea se utiliza para traer el elemento al cual observaremos, ese elemento no lo hemos creado todavía en nuestro HTML pero tendrá un id de observer.

Por último le instruiremos a nuestro objeto observer que debe observar el elemento.

Ahora vamos a crear el elemento de HTML el cual será un div y tendrá un id llamado 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>

Ahora que tenemos nuestro observer configurado vamos a crear la llamada al API, utilizaremos un API publico llamado instawebtools. En el área de métodos en nuestra instancia de Vue vamos a añadir el siguiente método:

  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)
                })
      }
  },

Ahora vamos a nuestro data object en la instancia de Vue y añadimos las propiedades que utilizamos en este método.

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

Ahora vamos a añadir en nuestro callback function del intersection observer las siguientes líneas.

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

Cada vez que este callback sea ejecutado y la propiedad isIntersecting sea cierta vamos a llamar el método que nos trae la lista del API y sumarle uno al número de pagina.

Por último vamos a presentarle al usuario la 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>

Aquí todo el código.

<!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>

Espero que les haya ayudado este post!, Estaré explorando otros Browser APIs y escribiendo posts sobre ellos.

Categoria: javascriptvuejs