Precargar imágenes con JavaScript

js-preload

Dicen las teorías modernas del desarrollo web, de SEO y demás, que la rapidez para mostrar una página web es de los aspectos más determinantes para mantener a los usuarios en nuestro sitio. Afirmando incluso que si la página no se carga en menos de dos segundos, el usuario se irá y no volverá a nuestro sitio. Tal vez yo soy muy paciente, porque esto me parece una exageración. Pero hagamos de cuenta que les creemos.

De las cosas que ralentizan la carga de un sitio web, las imágenes son tal vez de lo más crítico. Sobre todo en estos tiempos donde los desarrolladores nos damos la libertad de agregar imágenes grandes, de alta calidad, como parte del diseño, sin considerar factores como el ancho de banda del usuario, o la capacidad de transferencia del servidor o hosting, nos arriesgamos a dar una mala experiencia de uso a nuestros visitantes.

js-preload-01

Sin importar las técnicas de optimización que utilicemos para reducir el peso de la imagen, este riesgo seguirá existiendo, y no estamos exentos a que las imágenes tarden en cargar. Lo que podemos hacer, siguiendo las buenas prácticas de los sitios más populares (y más llenos de imágenes) en la actualidad, es precargar las imágenes antes de mostrarlas.

Precargado de recursos en HTML

Antes de ir a la solución, vamos a mencionar un aspecto que ya es parte del estándar HTML, que es el precargado de recursos usando la etiqueta <link>. Esto se hace para que el navegador comience a descargar elementos antes de que sean requeridos por la página. Se puede usar, por ejemplo, para precargar hojas de estilo antes de utilizarlas.

<link rel="preload" href="styles.css" as="style">
<link rel="stylesheet" href="styles.css">

La idea con esto, es que el recurso esté disponible para cuando sea requerido en el documento. Se le indica al navegador que descargue la hoja de estilos en segundo plano mientras se continúa el renderizado de la página.

El atributo as tiene diferentes valores para los distintos tipos de recursos que podemos precargar con este método. Uno de estos tipos es "image", precisamente. Sin embargo, esto no es de mucha ayuda si la imagen tarda en cargarse. Si el navegador llega al punto donde requiere la imagen, y ésta no ha sido precargada, tendremos el mismo problema.

Esto no es una solución para lo que nos concierne en este tema. Lo que haremos, entonces, es precargar la imagen con unas cuantas líneas de JavaScript.

Precargar imágenes con JavaScript

Los sistemas de servidor, generalmente, vienen configurados para guardar en caché archivos de recursos comunes, como imágenes, hojas de estilo, scripts, etcétera; esto con la finalidad de reducir solicitudes al servidor cuando necesite los mismos recursos en diferentes páginas, agilizando al mismo tiempo la experiencia del usuario.

Nuestro problema es cuando la imagen aún no está en caché, cuando tiene que descargarse del servidor. Una vez hecha la primera descarga no tendremos que preocuparnos por esto, mientras el caché no expire.

Entonces, lo que necesitamos desde JavaScript es simplemente hacer una llamada a la URL de la imagen en el servidor, para que ésta se descargue una vez, y las siguientes solicitudes traerán la imagen del caché del navegador, incluyendo la imagen que se mostrará en la página.

function preload(image, url) {
  var xhr = new XMLHttpRequest();
  xhr.open('get', url);
  xhr.onloadend = function () {
    image.src = url;
  };
}

La función preload recibe dos parámetros: el elemento HTMLImage y la URL de la imagen. Hacemos la solicitud usando un objeto XMLHttpRequest y una vez que termine de descargarse, asignamos la URL al elemento.

También podemos hacerlo con la API fetch, si queremos usar código más “moderno”:

function preload(image, url) {
  fetch(url).then(request => request.blob()).then(() => {
    image.src = url;
  });
}

Hay que asegurarnos que nuestro servidor guarde las imágenes en caché. Generalmente así es, pero no está de más verificarlo. Ya con esto, mientras usemos la misma URL, estaremos recibiendo el archivo de caché.

Demo

El código del demo está en GitHub:

https://github.com/israel-munoz/js-image-preloader

Es un sitio sencillo, hecho en Node usando Express.js únicamente para simular la descarga lenta de imágenes. Puedes checar el método para esta simulación en routes/images.js.

El sitio cuenta con una página principal (únicamente para navegación), y dos páginas con el mismo contenido: una imagen en una etiqueta <img> y otra como background-image de un elemento <div>. En la primera página, las imágenes se cargan durante el renderizado, por lo que vemos como se dibujan por partes. En la segunda página, los elementos tienen una animación de espera, en lo que la imagen se precarga en JavaScript, para después mostrar la imagen completa.

js-preload-02

La función preload es un poco diferente a lo que vimos anteriormente, para distinguir si la imagen se asignará como atributo src del elemento o como background-image de los estilos. No es mucha diferencia, y la base es la misma.

En las herramientas de desarrollo (disponibles en todos los navegadores principales), podemos ver cómo se maneja cada solicitud.

js-preload-03

Como vemos, hay dos peticiones por cada imagen. Una que hace la descarga del archivo, en las cuales podemos ver el tamaño y la duración de la descarga (663KB en 3.34 segundos, y 171KB en 871 milisegundos), y la segunda solicitud de cada una no tiene tamaño de descarga, pues la obtuvo del caché, y tomó una mínima fracción de segundo, lo que requirió para desplegarla en pantalla.

Si recargamos la página, apenas si veremos la imagen de Loading, pues inmediatamente traerá la imagen correspondiente del caché y la mostrará en la página. Si vemos el inspector de la red nos mostrará que las dos solicitudes por cada imagen se hicieron al caché, y ninguna se hizo al servidor.

js-preload-04

Siempre que lo necesitemos, podemos usar Ctrl+F5 o Ctrl+Shift+R para que se recargue la página ignorando el caché del sitio, lo cual nos dará el mismo resultado de la primera prueba.

Extensibilidad

Ya que estamos usando JavaScript para detectar el estatus de la imagen, podemos extenderlo de muchas formas. Por ejemplo, si queremos mostrar el progreso de la descarga, como porcentaje o una barra de progreso, podemos manejar el evento onprogress del objeto XMLHttpRequest. O si la imagen no puede cargarse, ya sea por que no se encuentre o haya un error durante la descarga, podemos mostrar un mensaje, o una imagen predeterminada que indique si hubo un error.

A fin de cuentas, lo importante es mejorar la experiencia del usuario, y JavaScript nos da infinitas posibilidades para ello.

 

Imagen del encabezado: Sand Clock with White Sand https://www.shutterstock.com/

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión /  Cambiar )

Google photo

Estás comentando usando tu cuenta de Google. Cerrar sesión /  Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión /  Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión /  Cambiar )

Conectando a %s