Carga diferida de imágenes
Las imágenes pueden aparecer en una página web debido a que están en línea (in-line) en el HTML como <img>
o como imágenes de fondo CSS. En esta publicación, descubrirás cómo cargar de forma diferida ambos tipos de imagen.
Imágenes en línea #
Los candidatos de carga diferida más comunes son las imágenes que se usan en los elementos <img>
. Con las imágenes en línea, tenemos tres opciones para una carga diferida que se pueden usar en combinación para obtener la mejor compatibilidad entre los navegadores:
Usar la carga diferida a nivel del navegador #
Tanto Chrome como Firefox permiten la carga diferida con el atributo de loading
. Este atributo se puede agregar a los <img>
y también a los elementos <iframe>
. Un valor de lazy
le dice al navegador que cargue la imagen inmediatamente si está en la ventana gráfica y que busque otras imágenes cuando el usuario se desplace cerca de ellas.
Consulta el campo de loading
de la tabla de compatibilidad de navegadores de MDN para obtener detalles sobre la compatibilidad de navegadores. Si el navegador no permite la carga diferida, el atributo se ignorará y las imágenes se cargarán inmediatamente, de manera habitual.
Para la mayoría de los sitios web, agregar este atributo a las imágenes en línea aumentará el rendimiento y evitará que los usuarios carguen imágenes a las que es posible que nunca se desplacen. Si tienes una gran cantidad de imágenes y deseas asegurarte de que los usuarios de navegadores sin compatibilidad para la carga diferida se beneficien, deberás combinar esto con uno de los métodos que se explican a continuación.
Para obtener más información, consulta Carga diferida a nivel del navegador para la web.
Usar Intersection Observer #
Para hacer un polyfill de carga diferida de los elementos de <img>
, usamos JavaScript para verificar si están en la ventana gráfica. Si es así, los atributos de src
(y algunas veces los atributos de srcset
) se completan con los URL para el contenido de imagen deseado.
Si has escrito código de carga diferida antes, es posible que hayas logrado tu tarea mediante el uso de controladores de eventos como scroll
o resize
. Si bien este enfoque es el más compatible en todos los navegadores, los navegadores modernos ofrecen una forma más eficaz y eficiente de realizar el trabajo de verificar la visibilidad de los elementos a través de la API de Intersection Observer.
Intersection Observer es más fácil de usar y leer que el código que se basa en varios controladores de eventos, porque solo necesita registrar un observador para mirar los elementos en lugar de escribir un tedioso código de detección de visibilidad de elementos. Todo lo que queda por hacer es decidir qué hacer cuando un elemento esté visible. Supongamos el siguiente patrón de marcado básico para tus elementos <img>
:
<img class="lazy" src="placeholder-image.jpg" data-src="image-to-lazy-load-1x.jpg" data-srcset="image-to-lazy-load-2x.jpg 2x, image-to-lazy-load-1x.jpg 1x" alt="¡Soy una imagen!">
Hay tres partes relevantes de este marcado en las que deberías de enfocarte:
- El
class
, que es con lo que seleccionará el elemento en JavaScript. - El
src
, que hace referencia a una imagen de reserva (placeholder) de posición que aparecerá cuando se cargue la página por primera vez. - Los atributos de
data-src
ydata-srcset
, los cuales son atributos que definen una reserva que contienen la URL de la imagen que cargará una vez que el elemento esté en la ventana gráfica.
Ahora veamos cómo usar Intersection Observer en JavaScript para cargar imágenes de forma diferida usando este patrón de marcado:
document.addEventListener("DOMContentLoaded", function() {
var lazyImages = [].slice.call(document.querySelectorAll("img.lazy"));
if ("IntersectionObserver" in window) {
let lazyImageObserver = new IntersectionObserver(function(entries, observer) {
entries.forEach(function(entry) {
if (entry.isIntersecting) {
let lazyImage = entry.target;
lazyImage.src = lazyImage.dataset.src;
lazyImage.srcset = lazyImage.dataset.srcset;
lazyImage.classList.remove("lazy");
lazyImageObserver.unobserve(lazyImage);
}
});
});
lazyImages.forEach(function(lazyImage) {
lazyImageObserver.observe(lazyImage);
});
} else {
// Possibly fall back to event handlers here
}
});
En el evento de DOMContentLoaded
del documento, este script consulta al DOM todos los <img>
con una clase de lazy
. Si Intersection Observer está disponible, crea un nuevo observador que ejecuta una retrollamada cuando los img.lazy
ingresen a la ventana gráfica.
Intersection Observer está disponible en todos los navegadores modernos. Por lo tanto, usarlo como polyfill para loading="lazy"
garantizará que la carga diferida esté disponible para la mayoría de los visitantes.
Imágenes en CSS #
Si bien las etiquetas de <img>
son la forma más común de usar imágenes en páginas web, las imágenes también se pueden invocar a través de la propiedad CSS de background-image
(y mediante otras propiedades). La carga diferida a nivel del navegador no se aplica a las imágenes de fondo del CSS, por lo que debes de considerar otros métodos si tienes imágenes de fondo para la carga diferida.
A diferencia de los elementos <img>
que se cargan independientemente de su visibilidad, el comportamiento de carga de imágenes en CSS se realiza con más especulación. Cuando se crea el documento y los modelos de objetos CSS y el render tree (árbol de renderización), el navegador examina cómo se aplica el CSS a un documento antes de solicitar recursos externos. Si el navegador ha determinado que una regla CSS que involucra un recurso externo no se aplica al documento como está construido actualmente, el navegador no lo solicita.
Este comportamiento especulativo se puede utilizar para diferir la carga de imágenes en CSS mediante el uso de JavaScript para determinar cuándo un elemento está dentro de la ventana gráfica y, posteriormente, aplicar una clase a ese elemento que aplica un estilo invocando una imagen de fondo. Esto hace que la imagen se descargue en el momento en que se necesite en lugar de en la carga inicial. Por ejemplo, tomemos un elemento que contiene una imagen de fondo de héroe grande:
<div class="lazy-background">
<h1>Here's a hero heading to get your attention!</h1>
<p>Here's hero copy to convince you to buy a thing!</p>
<a href="/buy-a-thing">Buy a thing!</a>
</div>
El elemento de div.lazy-background
normalmente contendría la imagen de fondo del héroe invocada por algún CSS. En este ejemplo de carga diferida, sin embargo, puedes aislar el div.lazy-background
de la propiedad del elemento de background-image
mediante una clase visible
añadida al elemento cuando está en la ventana gráfica:
.lazy-background {
background-image: url("hero-placeholder.jpg"); /* Imagen de reserva */
}
.lazy-background.visible {
background-image: url("hero.jpg"); /* Imagen final */
}
Desde aquí, usa JavaScript para verificar si el elemento está en la ventana gráfica (¡utilizando Intersection Observer!) y agrega la clase de visible
al div.lazy-background
en el momento que carga la imagen:
document.addEventListener("DOMContentLoaded", function() {
var lazyBackgrounds = [].slice.call(document.querySelectorAll(".lazy-background"));
if ("IntersectionObserver" in window) {
let lazyBackgroundObserver = new IntersectionObserver(function(entries, observer) {
entries.forEach(function(entry) {
if (entry.isIntersecting) {
entry.target.classList.add("visible");
lazyBackgroundObserver.unobserve(entry.target);
}
});
});
lazyBackgrounds.forEach(function(lazyBackground) {
lazyBackgroundObserver.observe(lazyBackground);
});
}
});
Bibliotecas de carga diferida #
Las siguientes bibliotecas se pueden utilizar para cargar imágenes de forma diferida.
- lazysizes es una biblioteca de carga diferida que contiene todas las funciones usadas para cargar imágenes e iframes de forma diferida. El patrón que usa es bastante similar a los ejemplos de código que se muestran aquí, ya que se vincula automáticamente a una clase de
lazyload
a<img>
y requiere que especifiques las URL de la imagen endata-src
y/odata-srcset
, el contenido de que se intercambian en lossrc
y/osrcset
respectivamente. Utiliza Intersection Observer (que puede usarse mediante un polyfill) y se puede ampliar con varios complementos para hacer cosas como cargas diferidas de videos. Obtén más información sobre el uso de lazysizes. - vanilla-lazyload es una opción ligera para imágenes de carga diferida, imágenes de fondo, videos, iframes y scripts. Este utiliza Intersection Observer, admite imágenes receptivas y permite la carga diferida a nivel del navegador.
- lozad.js es otra opción ligera que solo usa Intersection Observer. Como tal, tiene un alto rendimiento, pero se tiene que hacer un polyfill antes de poder usarlo en navegadores más antiguos.
- Si necesitas una biblioteca de carga diferida específica para React, considera react-lazyload. A pesar de que no utiliza Intersección Observer, este proporciona un método conocido de imágenes carga diferida para aquellos que están acostumbrados a desarrollar aplicaciones con React.