Haciendo un lazy load de imagenes con IntersectionObserver

Te habrás topado a la hora de optimizar un sitio web que servicios como Lighthouse te sugiere hacer un defer (lazy load o carga perezosa) de las imágenes que no son visibles recién se entra en un sitio web, es decir, las imágenes que están más abajo de la primer área visible en el sitio, pues aquí te mostraré una forma muy sencilla de arreglar esto.
En este ejemplo, que es una ampliación de uno anterior que había hecho (Haciendo un defer de las imágenes) utilizaremos la API del navegador llamada IntersectionObserver, esta API está soportada por cerca del 85% de los navegadores, y como aquí no hago discriminación, te enseñare además como hacer que funcione con el 15% restante de los navegadores viejos.

¿Que necesitamos?
1- Poder poner como imagen una que sea base64, es decir, que el src sea algo tipo: data:image/png;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs= en lugar de la ruta de la imagen, ¿esto para que? para cargar una mini imagen transparente y después, con javascript sustituirla por la imagen real.
2- Poder poner un atributo tipo data, por ejemplo data-src, donde pondremos la url real de la imagen.
3- Javascript, para hacer el cambio de la ruta.



Paso 1
Igual que en el artículo anterior, la etiqueta (o todas ellas) de la imagen deberá quedar algo así:
<img src="data:image/png;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=" data-src="images/imagen1.jpg" alt="Imagen" width="350" height="224">
Donde el valor del atributo data-src deberá ser el de la url de la imagen real, así como el alt y los tamaños. Esto hará 2 cosas principalmente, cargará una imagen muy ligera y asignará el tamaño final de la imagen al espacio donde se coloque la etiqueta.


Paso 2
En el artículo anterior les había compartido el siguiente script:
function init() {
	var imgDefer = document.getElementsByTagName('img');
	for (var i = 0; i < imgDefer.length; i++) {
		if (imgDefer[i].getAttribute('data-src')) {
			imgDefer[i].setAttribute('src', imgDefer[i].getAttribute('data-src'));
		}
	}
}
window.onload = init;​

Esto funciona bien pero es mejorable, este espera la carga completa del sitio para después cambiar las imágenes, el siguiente script hará un poco más, comprobará si el navegador soporta la API IntersectionObserver y de ser así la utilizará, de lo contrario utilizará el mismo script que habíamos usado antes.

El script completo queda así:
if ( "IntersectionObserver" in window ) {

	const lazyImages = document.querySelectorAll('[data-src]');

	const options = { threshold: 0 };

	const imageObserver = new IntersectionObserver(
		(entries, observer) => {
			entries.forEach(entry => {
				if (entry.isIntersecting) {
					const image = entry.target;
					image.src = image.dataset.src;
					imageObserver.unobserve(image);
				}
			});
		}, options);

	lazyImages.forEach(image => imageObserver.observe(image));

} else {

	function init() {
		var imgDefer = document.querySelectorAll('[data-src]');
		for (var i = 0; i < imgDefer.length; i++) {
			if (imgDefer[i].getAttribute('data-src')) {
				imgDefer[i].setAttribute('src', imgDefer[i].getAttribute('data-src'));
			}
		}
	}
	window.onload = init;

}​

Donde, por medio de los atributos data-src indicamos la imagen real, configuramos la API IntersectionObserver para que observe todas las imágenes que tengan ese atributo, para después, cuando estas son visibles sustituir la propiedad src con la indicada en data-src, es decir, en ambos casos el proceso es el mismo, solo que al usar la API solo hacemos en cambio en las imágenes conforme van siendo visibles por el navegador.

Como punto a destacar te informo que esta API tiene algunas configuraciones más, en este ejemplo yo solo utilice una llamada threshold poniendole un valor de cero, este valor va del 0 a 1 siendo un porcentaje visible de la imagen para que esta sea cambiada, es decir, el valor de cero le indica que en cuanto se vaya a mostrar se haga el cambio, en cambio, si le ponemos por ejemplo .5 significará que cuando la imagen sea visible en un 50% se ejecutará el cambio.

Para saber más sobre esta poderosa API te sugiero visites el siguiente sitio: https://developer.mozilla.org/es/docs/Web/API/Intersection_Observer_API

Si te sirvió este articulo por favor compártelo, si hay algo a mejorarle o comentarios por favor házmelos saber.