Imágenes bajo demanda con IntersectionObserver

Introducción a la API IntersectionObserver

Una manera de terminar de cargar mas rápido una página es evitar las llamadas a contenidos adicionales que no se requieran inmediatamente, o tal vez, nunca se requieran.

Que tal si en vez de cargar todas las imágenes de nuestra página al inicio, las cargamos sólo cuando el visitante requiere verlas porque está en su ventana de visualización (viewport)?

Hay una relativamente nueva API que viene a ayudarnos con este objetivo: Intersection Observer. Esta API nos permite detectar fácilmente cuando un elemento se encuentra visible, aunque sea parte de él (porcentualmente).

Definición

Lo primero es crear una nueva instancia pasándole una función callback que será ejecutada cada vez que el elemento observado:

  • ingrese parcialmente a la vista
  • salga completamente de la vista

También se le puede pasar un objeto con algunas opciones para cambiar el comportamiento por defecto que revisaremos mas adelante.

var imgObserver = new IntersectionObserver(function(changes) {
    changes.forEach(function(change) {
        console.log(change);
    });
}, {
    // opciones por defecto
});

Con el código anterior hemos definido un nuevo observer el que por cada elemento observado llamará a la función interna, la cual por el momento sólo hará un registro a la consola del objeto changes. Si revisamos la consola encontraremos registros como los siguientes:

Log IntersectionObserverEntry - IntersectionObserver

Con estos datos disponibles podremos manejar eficientemente la carga de imágenes sólo cuando el visitante necesite verlas.

Las opciones que tenemos disponibles son las siguientes:

  • threshold: Permite definir un arreglo de números decimales que representan los valores para el radio de intersección del elemento en los cuales se llamará a la función callback. El valor por defecto es [0].
  • root: Es el elemento usado como referencia para determinar las intersecciones de los elementos con la vista. El valor por defecto corresponde a la vista del documento de mayor nivel.
  • rootMargin: Permite agrandar o achica el box del elemento que se está observando y se ingresa con el mismo formato con el que se definen los márgenes en los archivos css. El valor por defecto es “0px 0px 0px 0px”.

Manos a la obra

En la portada de IntercambiaLáminas (https://www.intercambialaminas.com) se despliegan múltiples imágenes que no deseo cargar sino hasta que el visitante las tengo en su vista, por lo tanto es un buen caso para aplicar IntersectionObserver.

Portada IntercambiaLáminas sin IntersectionObserver

1.-
Lo primero es cambiar todos los atributos src de las imágenes por data-src para que no sean cargadas inicialmente, pero tengamos el dato disponible.

<img class="js_observed" data-src="{{::collection.image}}">

El {{::collection.image}} es porque estoy usando AngularJS el cual asigna una única vez el valor de esa variable que tiene la ruta dinámica a la imagen. La clase js_observed me sirve para después poder seleccionar fácilmente todos los elementos a los que le quiero aplicar esta técnica.

2.-
Definimos nuestro observer.

if ('IntersectionObserver' in window) {
    var imgObserver = new IntersectionObserver(function(changes) {
        changes.forEach(function(change) {
            if (change.isIntersecting) {
                change.target.src = change.target.dataset.src;
                imgObserver.unobserve(change.target);
            }
        });
    }, {
        // threshold: [0]
    });
} else {
    console.log('IntersectionObserver not supported');
}

Tenemos que asegurarnos que el navegador soporte esta nueva API, ya que de acuerdo a la data entregada por caniuse.com a mediados de junio del 2017 sólo hay un 53% de implementación global.

Luego por cada elemento observado verificamos que efectivamente se está visualizando con el change.isIntersecting y asignamos la URL del data-src al src, para finalmente dejar de observar el elemento con el método unobserve.

3.-
Seleccionamos las imágenes que observaremos

const imgs = Array.from(document.querySelectorAll(selector));
imgs.forEach(function(img) {
if ('IntersectionObserver' in window) {
    imgObserver.observe(img);    
} else {
    img.src = img.dataset.src;
}
});

Nuevamente verificamos que el navegador soporte nuestra API y si es así observamos el elemento con el método observe. En caso contrario asignamos en seguida el data-src al src.

Finalizando

Realizando los 3 pasos detallados anteriormente ya tenemos implementada nuestra carga de imágenes bajo demanda, ahora sólo se hará la petición del recurso cuando el visitante realmente requiera la imagen mejorando nuestros tiempos de carga inicial y disminuyendo la data solicitada lo que es fundamental para la navegación desde móviles.

Referencias

Agregar un comentario

Su dirección de correo no se hará público. Los campos requeridos están marcados *