web

Lazy loading de imágenes con Intersection Observer API

Introducción

Veamos en este pequeño tutorial cómo hacer lazy loading de imágenes con Intersection Observer API.

Así evitaremos cargar todas las páginas, sobrecargar nuestros servidores y gastar ancho de banda.

Probar página terminada

Puedes probar aquí la página terminada. Te recomiendo que abras la consola de depuración para ver que las imágenes se cargan conforme navegas.

¿Qué es el lazy loading?

El lazy loading es cargar las imágenes de una página web conforme el usuario las necesite.

Es decir, si tenemos una página con 100 imágenes el usuario no verá las 100 al mismo momento, sino que tal vez irá bajando poco a poco. Justo ahí entra en acción el observador de intersección, que nos dirá si el usuario está a punto de ver la imagen. En caso de que sí, la cargamos.

¿Qué es intersection observer API?

Es una nueva API de programación de JavaScript que nos dice si la pantalla visible del usuario está a punto de intersecar con algo.

Tiene múltiples usos, pero veremos cómo hacerlo para cargar imágenes conforme el usuario hace scroll.

Hace tiempo, cuando esta API no existía, no tengo idea de cómo se hacía pero supongo que se vigilaba el scroll, la altura del documento y muchísimas cosas más.

Pues Intersection observer viene a solucionarnos este problema. Ah, por cierto, recordemos que necesitamos un navegador actualizado.

Actualmente es compatible con Edge 12, Firefox y Chrome en sus últimas versiones. Puedes ver la lista aquí.

Ventajas

Como lo dije, ahorramos ancho de banda porque no requerimos todas las imágenes al mismo tiempo sin que el usuario las vaya a ver.

Y por otro lado, la página cargará más rápido.

Pero bueno, vamos allá.

Lazy loading de imágenes con Intersection Observer API

Lo que haremos será una simple página con texto e imágenes. Y las imágenes serán cargadas conforme el usuario haga scroll o navegue.

Documento HTML

Entonces tenemos nuestro documento HTML que queda así:

<!DOCTYPE html>
<html lang="es">
<head>
 <meta charset="UTF-8">
 <title>Lazy Loading de imágenes con Intersection Observer API</title>
</head>
<style>
 h1{
  font-size: 3em;
 }
 p{
  font-size: 1.4em;
 }
</style>
<body>
 <!-- Este div se repetirá en el verdadero proyecto, con imágenes distintas -->
 <div>
  <h1>Este es el título</h1>
  <p>
   <span>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Obcaecati similique illo amet natus, atque doloremque quos sunt officia eligendi, officiis fugiat possimus inventore. Dolor itaque, libero, delectus tenetur doloremque earum.</span>
   <span>Commodi dolor vel labore neque maiores, odio doloremque similique sequi modi. Quod cumque, sunt, vero dignissimos debitis facere reiciendis tenetur fugit modi, placeat nostrum iure sint assumenda. Temporibus, minus, alias!</span>
   <span>Blanditiis quo, adipisci consequuntur beatae fugiat rerum, optio? Dignissimos porro at nihil cupiditate, libero dicta ratione. Provident, qui expedita iure. Sunt temporibus quae dignissimos, sed saepe numquam modi ipsa dicta.</span>
   <span>Nobis nam sequi, maiores est vitae soluta at, suscipit molestiae enim a! Consequatur, ipsam. Nesciunt ea ullam neque illum, veritatis laborum eos laudantium ipsum necessitatibus nostrum quae quod omnis eum!</span>
   <span>Qui iusto eum similique distinctio nihil aut odio quibusdam tempora tempore beatae ad perferendis, natus, illum praesentium iste placeat mollitia sed deserunt totam autem ab suscipit, ea obcaecati. Eveniet, sed.</span>
   <br>
   <span>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Obcaecati similique illo amet natus, atque doloremque quos sunt officia eligendi, officiis fugiat possimus inventore. Dolor itaque, libero, delectus tenetur doloremque earum.</span>
   <span>Commodi dolor vel labore neque maiores, odio doloremque similique sequi modi. Quod cumque, sunt, vero dignissimos debitis facere reiciendis tenetur fugit modi, placeat nostrum iure sint assumenda. Temporibus, minus, alias!</span>
   <span>Blanditiis quo, adipisci consequuntur beatae fugiat rerum, optio? Dignissimos porro at nihil cupiditate, libero dicta ratione. Provident, qui expedita iure. Sunt temporibus quae dignissimos, sed saepe numquam modi ipsa dicta.</span>
   <span>Nobis nam sequi, maiores est vitae soluta at, suscipit molestiae enim a! Consequatur, ipsam. Nesciunt ea ullam neque illum, veritatis laborum eos laudantium ipsum necessitatibus nostrum quae quod omnis eum!</span>
   <span>Qui iusto eum similique distinctio nihil aut odio quibusdam tempora tempore beatae ad perferendis, natus, illum praesentium iste placeat mollitia sed deserunt totam autem ab suscipit, ea obcaecati. Eveniet, sed.</span>
   <br>
  </p>
  <img class="lazy-loading" src="" data-src="https://picsum.photos/200" alt="Una imagen">
 </div>
</body>
</html>

No prestemos atención a otra cosa que la imagen. Vemos que no tiene src, sino data-src. Esto es porque no queremos que el navegador descargue la imagen, dejamos vacío el atributo y ponemos la verdadera imagen en un atributo que el navegador no conoce; bueno, sí lo conoce pero no descargará la imagen de ahí.

Por otro lado vemos que la imagen tiene la clase lazy-loading. Esto es para que luego podamos recuperarlas a todas con querySelectorAll.

Es importante mencionar que vamos a repetir muchas veces el div. En el código de arriba no se repite porque sería muy largo pegarlo aquí en el post, pero vamos a repetirlo para que haya muchas imágenes y scroll.

Se darán cuenta de que lo cargamos de https://picsum.photos/200 (un servicio que da imágenes para probar). Pues podemos cambiar el 200 por otro número y ese será el tamaño de la imagen en pixeles.

Adicionalmente podemos anexar otro número después del 200 (por ejemplo 200/100) para la otra medida.

Recuperar imágenes

Ahora vamos a recuperar todas las imágenes que tengan la clase lazy-loading. Para ello esperaremos el evento DOMContentLoaded que es como el document ready de jQuery.

Más tarde usamos document.querySelector y le pasamos el selector de img que tenga la clase lazy-loading (img.lazy-loading)

document.addEventListener("DOMContentLoaded", function () {
    var $imagenes = document.querySelectorAll("img.lazy-loading");

});

Te preguntarás por qué pongo un $ antes del nombre de la variable. Y es porque tengo la costumbre de que a los elementos del DOM les pongo dicho símbolo para identificarlos como elementos, no como variables que uso en el programa.

Es una práctica mía, no importa el nombre

Comprobar IntersectionObserver y observar

Ahora comprobamos si el navegador tiene soporte para observar la intersección. Y en caso de que sí, creamos un observador y por cada entrada que tenga añadimos un callback.

Dicho callback es llamado cuando hay una intersección con un elemento.

Comparamos si el ratio es mayor a 0 (lo que significa que el usuario va a ver la imagen) y a la imagen le ponemos el atributo src copiando lo que había en data-src:

if ("undefined" !== typeof IntersectionObserver) {
    var observador = new IntersectionObserver(function (entradas) {
        for (var i = 0; i < entradas.length; entradas++) {
            var entrada = entradas[i];
            if (entrada.intersectionRatio > 0) {
                var imagen = entrada.target;
                imagen.src = imagen.dataset.src;
                observador.unobserve(imagen);
            }
        }
    });
} else {
    for (var j = 0; j < $imagenes.length; j++) {
        $imagenes[j].src = $imagenes[j].dataset.src;
    }
}

Por cierto, en caso de que no exista la API en el navegador entonces cargamos la fuente de la imagen normalmente; es decir, no hay lazy-loading pero las imágenes sí se verán (aunque cargarán de modo convencional)

Finalmente dejamos de observar a la imagen con:

observador.unobserve(imagen);

Porque una vez cargada la imagen ya no necesitaremos vigilarla.

Observar imágenes

Ya programamos el callback de la imagen y dijimos que la imagen será cargada y dejada de observar. Lo único que falta ahora es observarlas para que dicho callback sea llamado:

for (var x = 0; x < $imagenes.length; x++) {
    observador.observe($imagenes[x]);
}

Con eso vamos a observar cada imagen.

Código completo

El código completo de ejemplo queda así:

document.addEventListener("DOMContentLoaded", function () {
    var $imagenes = document.querySelectorAll("img.lazy-loading");
    if ("undefined" !== typeof IntersectionObserver) {
        var observador = new IntersectionObserver(function (entradas) {
            for (var i = 0; i < entradas.length; entradas++) {
                var entrada = entradas[i];
                if (entrada.intersectionRatio > 0) {
                    var imagen = entrada.target;
                    imagen.src = imagen.dataset.src;//src = data-src
                    console.log("Cargada: ", imagen.src)
                    observador.unobserve(imagen);
                }
            }
        });
        for (var x = 0; x < $imagenes.length; x++) {
            observador.observe($imagenes[x]);
        }
    } else {
        //En caso de que no exista la API
        for (var j = 0; j < $imagenes.length; j++) {
            $imagenes[j].src = $imagenes[j].dataset.src;
        }
    }
});

Y con eso, cualquier imagen que tenga la clase será cargada con lazy-loading.

Probar documento

Si ahora abrimos nuestro documento, abrimos la consola y navegamos, veremos que la imagen se irá cargando conforme hacemos scroll o mejor dicho, mientras navegamos ya sea con el teclado, el touch o el mouse.

Informe de imagen cargada con Lazy Loading

Recuerda que puedes probar el proyecto terminado en mi página web.

Así es como podemos hacer lazy loading de imágenes con JavaScript sin quebrarnos la cabeza.

Puedes también, en la consola, abrir la pestaña de Network y ver que las imágenes no se cargan al mismo tiempo.

Estoy aquí para ayudarte 🤝💻


Estoy aquí para ayudarte en todo lo que necesites. Si requieres alguna modificación en lo presentado en este post, deseas asistencia con tu tarea, proyecto o precisas desarrollar un software a medida, no dudes en contactarme. Estoy comprometido a brindarte el apoyo necesario para que logres tus objetivos. Mi correo es parzibyte(arroba)gmail.com, estoy como@parzibyte en Telegram o en mi página de contacto

No te pierdas ninguno de mis posts 🚀🔔

Suscríbete a mi canal de Telegram para recibir una notificación cuando escriba un nuevo tutorial de programación.
parzibyte

Programador freelancer listo para trabajar contigo. Aplicaciones web, móviles y de escritorio. PHP, Java, Go, Python, JavaScript, Kotlin y más :) https://parzibyte.me/blog/software-creado-por-parzibyte/

Ver comentarios

  • Muy interesante tu código, no tienes uno que funcione con srcset ? he visto varios pero todos muy enredados

    • Me parece que no es recomendable usar srcset con JavaScript. Lo recomendable sería usar algo así como el lazy loading pero detectar el tamaño de la pantalla del dispositivo y a partir de ello cargar una imagen con el tamaño apropiado

Entradas recientes

Tramitar acta de nacimiento en línea de manera instantánea

En este post te quiero compartir mi experiencia tramitando un acta de nacimiento de México…

1 hora hace

Creador de credenciales web – Aplicación gratuita

Hoy te voy a presentar un creador de credenciales que acabo de programar y que…

2 semanas hace

Desplegar PWA creada con Vue 3, Vite y SQLite3 en Apache

Ya te enseñé cómo convertir una aplicación web de Vue 3 en una PWA. Al…

2 semanas hace

Arquitectura para wasm con Go, Vue 3, Pinia y Vite

En este artículo voy a documentar la arquitectura que yo utilizo al trabajar con WebAssembly…

2 semanas hace

Vue 3 y Vite: crear PWA (Progressive Web App)

En un artículo anterior te enseñé a crear un PWA. Al final, cualquier aplicación que…

2 semanas hace

Errores de Comlink y algunas soluciones

Al usar Comlink para trabajar con los workers usando JavaScript me han aparecido algunos errores…

2 semanas hace

Esta web usa cookies.