Lazy Loading elemento de imagen HTML5

8 minutos de lectura

avatar de usuario
mrray

He estado buscando (sin éxito) un método confiable para cargar imágenes de forma diferida mientras uso la especificación HTML5 para <picture>. La mayoría de las soluciones/complementos que existen actualmente se basan en el uso data- atributos Podría estar equivocado, pero no parece que este método funcione en conjunto con <picture>.

Realmente solo busco que me apunten en la dirección correcta. Si alguien tiene una solución que esté usando actualmente, me encantaría verla. ¡Gracias!

Aquí está el marcado estándar según la especificación HTML5:

<picture width="500" height="500">
    <source media="(min-width: 45em)" src="large.jpg">
    <source media="(min-width: 18em)" src="med.jpg">
    <source src="small.jpg">
    <img src="small.jpg" alt="">
</picture>

  • ¿Puedes mostrar tu marcado, por favor? Por carga diferida, te refieres a “carga cuando está en la ventana gráfica“? ¿Qué métodos ha encontrado? ¿Puede publicar uno y mostrar cómo podría aplicarse a <pictures> (y por qué eso no funcionaría)?

    – Bergi

    3 de junio de 2014 a las 21:49

  • Sí, lo siento, hice la suposición. Por carga diferida de imágenes, me refiero a cuando están en la ventana gráfica. La solución más común para las imágenes que he visto es Complemento de carga diferida para jQuery.

    – Señor Ray

    3 de junio de 2014 a las 21:54

  • Hm, ese complemento no es mágico. Podrías adaptarlo fácilmente para trabajar con <source> elementos de una imagen, en lugar de utilizar el src atributo de una img.

    – Bergi

    3 de junio de 2014 a las 22:04

  • @MrRay No sé cómo analizan los navegadores (o analizarán) <picture> etiqueta, pero lo más probable es que la imagen apropiada se cargue durante el análisis html (como lo hacen actualmente para <img> etiqueta), por lo que la única forma de detenerlo es usar data-src atributo en lugar de src en <source> etiqueta. Pero incluso en este caso hay algunos problemas, por lo que en LazyLoadXT empezamos a usar <br> etiqueta en lugar de <source>: ressio.github.io/lazy-load-xt/demo/picture.htm (No recuerdo exactamente cuál era el problema con <source> etiqueta, pero de esa manera no funcionó en algunos navegadores, mientras que <br> hizo)

    – Denis Riabov

    5 de junio de 2014 a las 17:49

  • Gracias @DenisRyabov. Sí, ese es exactamente el problema que estoy tratando de resolver. He visto este complemento: es el único que soluciona el problema. Ahora entiendo por qué usa el <br> ¡etiqueta!

    – Señor Ray

    6 de junio de 2014 a las 18:29

avatar de usuario
dai

Ahora es febrero de 2020 y me complace informar que Google Chrome, Microsoft Edge (el Edge basado en Chromium) y Mozilla Firefox todos apoyan lo nuevo loading="lazy" atributo. El único navegador moderno que se resiste es Safari de Apple (tanto iOS Safari como macOS Safari) pero recientemente terminaron de agregarlo a la base de código de Safariasí que espero que sea lanzado en algún momento de este año.

los loading="lazy" atributo es sólo para el <img /> elemento (y no <picture>) pero recuerda que el <picture> elemento no representa el real contenido reemplazadola <img /> el elemento lo hace (es decir, la imagen que los usuarios ven siempre es representada por el <img /> elemento, el <picture> elemento solo significa que el navegador puede cambiar el <img src="" /> atributo. De la especificación HTML5 a partir de febrero de 2020 (énfasis mío):

los picture elemento es algo diferente del aspecto similar video y audio elementos. Si bien todos ellos contienen source elementos, el source elementos src atributo no tiene significado cuando el elemento está anidado dentro de un picture y el algoritmo de selección de recursos es diferente. También el picture el elemento en sí no muestra nada; simplemente proporciona un contexto para su contenido img elemento que le permite elegir entre varias URL.

Así que hacer esto debería solo trabajo:

<picture>
    <source media="(min-width: 45em)" srcset="large.jpg" />
    <source media="(min-width: 18em)" srcset="med.jpg" />
    <source src="small.jpg" />
    <img src="small.jpg" alt="Photo of a turboencabulator" loading="lazy" />
</picture>

Tenga en cuenta que la <picture> elemento no tiene ninguna width o height atributo propio; en cambio el width y height los atributos deben ser aplicados al niño <source> y <img> elementos:

4.8.17 Atributos de dimensión

los width y height atributos en img, iframe, embed, object, video, source cuando el padre es un picture elemento […] se puede especificar para dar las dimensiones del contenido visual del elemento (el ancho y la altura respectivamente, en relación con la dirección nominal del medio de salida), en píxeles CSS.

[…]

Los dos atributos deben omitirse si el recurso en cuestión no tiene un ancho intrínseco y una altura intrínseca.

Así que si quieres todo <source> imágenes que se renderizarán como 500px por 500px luego aplique el tamaño a la <img> elemento solamente (y no olvide el alt="" texto para usuarios con problemas de visión, es incluso un requisito legal en muchos casos):

<picture>
    <source media="(min-width: 45em)" srcset="large.jpg" />
    <source media="(min-width: 18em)" srcset="med.jpg" />
    <source src="small.jpg" />
    <img src="small.jpg" alt="Photo of a turboencabulator" loading="lazy" width="500" height="500" />
</picture>

  • @KyleMit gracias, estaba haciendo malabarismos con varios problemas, quería haber vinculado a stackoverflow.com/questions/2321907/…

    – Ciro Santilli Путлер Капут 六四事

    9 oct 2020 a las 11:58

  • ¿Significa esto que los atributos width y height también podría estar en el img?

    –Lucas David Ferrero

    11 de marzo a las 18:05

Para cualquiera que todavía esté interesado… Después de revisar este problema, me encontré con un script bastante nuevo llamado, Lazysizes. En realidad, es bastante versátil, pero lo que es más importante, me permite realizar una carga diferida de imágenes mientras utilizo el marcado HTML5 como se describe en el OP.

Muchas gracias al creador de este script, @afarkas.

avatar de usuario
Sean Doherty

Ejemplo de trabajo de imágenes de carga diferida usando el imagen elemento y observador de intersección Probado en Chrome y Firefox. Safari no admite el observador de intersecciones, por lo que las imágenes se cargan inmediatamente e IE11 no admite el elemento, por lo que recurrimos al valor predeterminado imagen

Las consultas de medios en el attr de medios son arbitrarias y se pueden configurar para adaptarse.

El umbral de ancho establecido es de 960 px: intente recargar por encima y por debajo de este ancho para ver la variación mediana (-m) o grande (-l) de la imagen que se descarga cuando la imagen se desplaza hacia la ventana gráfica.

Código abierto

<!-- Load images above the fold normally -->
<picture>
  <source srcset="https://stackoverflow.com/img/city-m.jpg" media="(max-width: 960px)">
  <source srcset="https://stackoverflow.com/questions/24025464/img/city-l.jpg" media="(min-width: 961px)">
  <img class="fade-in" src="https://stackoverflow.com/questions/24025464/img/city-l.jpg" alt="city"/>
</picture>

<picture>
  <source srcset="https://stackoverflow.com/img/forest-m.jpg" media="(max-width: 960px)">
  <source srcset="https://stackoverflow.com/img/forest-l.jpg" media="(min-width: 961px)">
  <img class="fade-in" src="https://stackoverflow.com/img/forest-l.jpg" alt="forest"/>
</picture>

<!-- Lazy load images below the fold -->
<picture class="lazy">
  <source data-srcset="https://stackoverflow.com/img/river-m.jpg" media="(max-width: 960px)">
  <source data-srcset="https://stackoverflow.com/https://stackoverflow.com/img/river-l.jpg" media="(min-width: 961px)">
  <img data-srcset="https://stackoverflow.com/https://stackoverflow.com/img/river-l.jpg" alt="river"/>
</picture>

<picture class="lazy">
  <source data-srcset="https://stackoverflow.com/img/desert-m.jpg" media="(max-width: 960px)">
  <source data-srcset="https://stackoverflow.com/https://stackoverflow.com/img/desert-l.jpg" media="(min-width: 961px)">
  <img data-srcset="https://stackoverflow.com/https://stackoverflow.com/img/desert-l.jpg" alt="desert"/>
</picture>

y el JS:

    document.addEventListener("DOMContentLoaded", function(event) {
   var lazyImages =[].slice.call(
    document.querySelectorAll(".lazy > source")
   )

   if ("IntersectionObserver" in window) {
      let lazyImageObserver = 
       new IntersectionObserver(function(entries, observer) {
          entries.forEach(function(entry) {
           if (entry.isIntersecting) {      
              let lazyImage = entry.target;
              lazyImage.srcset = lazyImage.dataset.srcset;
              lazyImage.nextElementSibling.srcset = lazyImage.dataset.srcset;
              lazyImage.nextElementSibling.classList.add('fade-in');
              lazyImage.parentElement.classList.remove("lazy");
             lazyImageObserver.unobserve(lazyImage);
            }
         });
        });

      lazyImages.forEach(function(lazyImage) {
       lazyImageObserver.observe(lazyImage);
      });
   } else {
     // Not supported, load all images immediately
    lazyImages.forEach(function(image){
        image.nextElementSibling.src = image.nextElementSibling.dataset.srcset;
      });
    }
  });

Un último pensamiento es que si cambia el ancho de la pantalla de un lado a otro, los archivos de imagen se descargan repetidamente nuevamente. Si pudiera vincular el método anterior a una verificación de caché, entonces esto sería oro…

En web.dev, Google advierte que las etiquetas de imágenes se pueden cargar de forma diferida.
https://web.dev/browser-level-image-lazy-loading/

Las imágenes que se definen usando el elemento también se pueden cargar de forma diferida:

<picture>
  <source media="(min-width: 800px)" srcset="large.jpg 1x, larger.jpg 2x">
  <img src="photo.jpg" loading="lazy">
</picture>

Aunque un navegador decidirá qué imagen cargar desde cualquiera de los elementos, el atributo de carga solo debe incluirse en el elemento alternativo.

¿Ha sido útil esta solución?