Lazy loading per immagini e video

Lazy Loading

Durante la costruzione di una pagina è molto difficile prevedere la priorità di caricamento degli elementi, sopratutto dei più pesanti come capita per le immagini, motivo per cui si tende ad utilizzare il lazy loading, così che il peso delle immagini caricate non influisca negativamente sulla performance del sito.

Questo post contiene i metodi di lazy loading più utilizzati, oltre ad introdurre il nuovo attributo loading, valido per <img><iframe>:

<img src="immagine-tipo.jpg" loading="lazy" alt="immagine tipo" />
<iframe src="pagina-tipo.html" loading="lazy"></iframe>

Che cos’è il lazy loading?

Il lazy loading è una operazione che si occupa di caricare le immagini e i video solo quando quest’ultimi sono visualizzabili, e non durante il caricamento iniziale della pagina. In questo modo il caricamento delle pagine è più veloce e le risorse in questione, video o immagini, vengono richiamate solo se è necessario.

L’utilizzo di questa tecnica velocizza il caricamento della pagina, influendo positivamente sulla User Experience dei siti più lenti e sulla velocità di scansione dei bot, tra cui Googlebot.

Le tecniche di Lazy Loading incidono sul SEO?

Per comprendere quanto le immagini fuori vistano possano rallentare il caricamento delle pagine basta analizzare il sito con gli strumenti presenti su Chrome, come Lighthouse, Performance e Network, in grado di offrire diverse prospettive sui tempi di caricamento delle immagini e dei video.

Durante le operazioni di ottimizzazioni del sito, un consulente SEO controlla più volte la performance del sito, utilizzando diversi strumenti, ma sebbene le operazioni siano sempre simili, non è possibile quantificare uniformemente il vantaggio ottenuto dall’operazione, se ci riferiamo al posizionamento.

Non bisogna però dimenticare che questa tecnica è solo una delle tante da applicare per le immagini, le altre (vedi per esempio il cambio formato delle immagini) possono essere altrettanto valide

Quali strumenti applicano il lazy loading?

Esistono alcune librerie JavaScript che consentono alle pagine di caricare le immagini con il lazy loading, ma cominciano a comparire anche supporti nativi dei browser. 

Il metodo nativo apre la possibilità ai browser di trovare e caricare in lazy loading i contenuti adatti ad essere visualizzati solo successivamente, senza richiedere agli sviluppatori della pagina web di far nient’altro se non inserire un’attributo.

Se per gli sviluppatori navigati il cambio di applicazione cambia poco a livello di risultati, per i neofiti un’implementazione più semplice può fare la differenza.

Lazy loading nel tag <img>

Le operazioni tradizionali di lazy loading tramite javascript si basano sulla creazione di un metodo che permette il caricamento delle immagini solo tramite il funzionamento di uno script che ne conceda la possibilità.

Per far si che questo accada, bisogna impedire che il src del tag immagine sia raggiungibile. Per questo motivo si utilizza al suo posto data-src, come nell’esempio:

<img data-src = "https://mio-sito.it/image.jpg" />

Successivamente basterà permettere al browser di caricare le informazioni dell’immagine solo quando le sue indicazioni saranno presenti nel viewport.

Per far si che questo accada, bisogna impostare del codice che rilevi la presenza di eventuali variazioni nella modalità di visualizzazione.

Gli esempi più semplici di tale “variazioni” sono lo scroll della pagina, la modifica di visualizzazione della risoluzione dello schermo, come accade quando si ruota un cellulare da verticale ad orizzontale.

E infine, bisogna selezionare quali immagini debbano subire questo trattamento, e quali no. Un buon modo è riconoscere i file tramite una classe CSS, come nell’esempio:

<img class="lazy" data-src="https://mio-sito.it/images/image.jpg" />

Per quanto riguarda la sezione di codice Javascript, si può tranquillamente utilizzare una forma simile (o identica) a questa:

document.addEventListener("DOMContentLoaded", function() {
  var lazyloadImages = document.querySelectorAll("img.lazy");    
  var lazyloadThrottleTimeout;
  
  function lazyload () {
    if(lazyloadThrottleTimeout) {
      clearTimeout(lazyloadThrottleTimeout);
    }    
    
    lazyloadThrottleTimeout = setTimeout(function() {
        var scrollTop = window.pageYOffset;
        lazyloadImages.forEach(function(img) {
            if(img.offsetTop < (window.innerHeight + scrollTop)) {
              img.src = img.dataset.src;
              img.classList.remove('lazy');
            }
        });
        if(lazyloadImages.length == 0) { 
          document.removeEventListener("scroll", lazyload);
          window.removeEventListener("resize", lazyload);
          window.removeEventListener("orientationChange", lazyload);
        }
    }, 20);
  }
  
  document.addEventListener("scroll", lazyload);
  window.addEventListener("resize", lazyload);
  window.addEventListener("orientationChange", lazyload);
});
lazy loading in background

Caricare immagini utilizzate come background col CSS

Ad una prima analisi, non c’è molta differenza tra il lazy loading applicato ad un’immagine utilizzata come background e ad una normale, se non fosse che nel primo caso il browser deve aver caricato il relativo DOM (Document Object Model) e avere ricevuto anche l’equivalente segnalazione per quanto riguarda la struttura CSS (CSSOM).

Se però l’elemento segnalato nel CSS non si risolve all’inizio del caricamento della pagina, sarà possibile farlo in un secondo momento utilizzando una certa modalità di lazy loading.

La gestione delle immagini per il browser è molto semplice, visto che non fa altro che vedere se sono disponibili e renderle visibili.

Viste le premesse, i metodi per “ostacolare” la visualizzazione di un’immagine da parte del browser è molto semplice.

Per farlo, basta inserire una classe che permetta il riconoscimento dell’elemento da attivare in un secondo momento, e segnalare tramite un id CSS l’immagine in background da inserire.

  <div id="img-background" class="lazy"></div>

Poi occorre aggiungere del CSS dedicato all’id, come di seguito, oltre ad un’indicazione specifica per una classe da inserire, il cui utilizzo sarà chiaro poco sotto:

#img-background.lazy {
   background-image: none;
   background-color: #000000;

#img-background {
  background-image: url("https://mio-sito.it/image10.jpg");
  max-width: 1200px;
  height: 600px;
}

Per ultimo bisogna inserire il codice Javascript che vada ad operare sulla classe “lazy”:

document.addEventListener("DOMContentLoaded", function() {
  var lazyloadImages;    

  if ("IntersectionObserver" in window) {
    lazyloadImages = document.querySelectorAll(".lazy");
    var imageObserver = new IntersectionObserver(function(entries, observer) {
      entries.forEach(function(entry) {
        if (entry.isIntersecting) {
          var image = entry.target;
          image.classList.remove("lazy");
          imageObserver.unobserve(image);
        }
      });
    });

    lazyloadImages.forEach(function(image) {
      imageObserver.observe(image);
    });
  } else {  
    var lazyloadThrottleTimeout;
    lazyloadImages = document.querySelectorAll(".lazy");
    
    function lazyload () {
      if(lazyloadThrottleTimeout) {
        clearTimeout(lazyloadThrottleTimeout);
      }    

      lazyloadThrottleTimeout = setTimeout(function() {
        var scrollTop = window.pageYOffset;
        lazyloadImages.forEach(function(img) {
            if(img.offsetTop < (window.innerHeight + scrollTop)) {
              img.src = img.dataset.src;
              img.classList.remove('lazy');
            }
        });
        if(lazyloadImages.length == 0) { 
          document.removeEventListener("scroll", lazyload);
          window.removeEventListener("resize", lazyload);
          window.removeEventListener("orientationChange", lazyload);
        }
      }, 20);
    }

    document.addEventListener("scroll", lazyload);
    window.addEventListener("resize", lazyload);
    window.addEventListener("orientationChange", lazyload);
  }
})

Tutta l’operazione si basa sul fatto che inizialmente l’indicazione background-image:none sia considerata la scelta primaria, e che solo in un secondo momento (dopo uno scroll o durante qualsiasi altra operazione segnalata nello script) il caricamento riguardi l’id e non le istruzioni che riguardano anche la classe.

In questo caso quindi, l’operazione avrà come effetto quello di caricare un’immagine in background.

Lazy Loading per i video

Con un sistema non molto differente, è possibile applicare la tecnica utilizzata per le immagini ai video.

In questo caso, come segnalato in questo articolo, basterà inserire il video all’interno di un tag , segnalare cosa visualizzare dentro gli attributi data-src che vanno al posto dei semplici src, e poi assegnare dei src che vadino a sostituire i data-src, in modo che in un secondo momento il video sia visibile.

Per informazioni più dettagliate sul codice, vi consiglio di utilizzare il codice presente su : https://gist.github.com/benjamingrobertson/00c5b47eaf5786da0759b63d78dfde9e

IntersectionObserver e librerie

Nel caso in cui i metodi utilizzati non siano di vostro gradimento, esistono dei metodi complicati ma altamente performanti che riguardano le API dell’IntersectionObserver e librerie come LazySizes, che ottimizzano i tempi di realizzazione della pratica e possono essere utilizzati così come sono.

Lazy Loading nativo

A partire da Chrome 76 è possibile utilizzare delle soluzioni molto più semplici per gestire la procedura di caricamento delle immagini e degli <iframe> in un secondo momento.

Per farlo, è necessario basta utilizzare l’attributo loading all’interno dell’elemento su cui si vuole operare.

L’attributo loading

L’ attributo loading consente a un browser di posticipare il caricamento degli elementi fino a quando gli utenti non effettuino uno scroll che li visualizzi. Per quanto riguarda l’utilizzo di loading, sebbene solo una delle impostazioni è valida per i nostri fini, vi riporto tutte e tre le diverse indicazioni:

  • lazy: il nome rende chiara la sua funzione.
  • eager: si occupa di caricare immediatamente la risorsa.
  • auto: utilizzandola, il browser determinerà se effettuare un caricamento tradizionale o meno.

Loading è efficace sia su <img> (incluso srcset e dentro <picture>) che su <iframe>, come segnalato alla pagina https://github.com/scott-little/lazyload :

<img src="image.jpg" loading="lazy" alt=".."/>
<img src="image.jpg" loading="eager" alt=".."/>
<img src="image.jpg" loading="auto" alt=".."/>

<picture>
  <source media="(min-width: 200px)" srcset="1.jpg 1x, 2.jpg 2x">
  <source srcset="3.jpg 1x, 4.jpg 2x">
  <img src="fallback.jpg" loading="lazy">
</picture>

<iframe src="lettore.html" loading="lazy"></iframe>

Per quanto sia facile e veloce utilizzare questo metodo, bisogna ricordare che l’attributo loading è un metodo non sempre applicabile. 

I browser che supportano l’attributo possono essere gestiti come indicato dalle righe di codice poco sopra, ma al momento questo sistema è compatibili con pochi browser (uno al momento), di conseguenza bisogna prendere in considerazione un metodo di caricamento ibrido, che includa anche il codice Javascript.

Browser multipli

Invece di inserire l’attributo loading, la soluzione più diffusa è quella descritta poco sopra e che utilizza il codice Javascript.

Un esempio di integrazione tra i sistemi potrebbe essere gestita in questo modo:

  • Le immagini nella parte alta (above the fold) dello schermo e che si trovano nel viewport sono normali <img>.
  • Si andrà invece ad utilizzare il data-src per le immagini che non hanno bisogno di un caricamento immediato, per tutti i browser non supportati dal loading. Nel caso in cui fosse supportato, scambieremmo data-src con src.
  • Se l’attributo loading non è supportato, bisogna allora caricare un fallback per tenere la forma e le dimensione dell’immagine prima che questa compaia. 
  • Basta utilizzare class=lazyload come segnalazione riguardante le immagini che vogliamo caricare in lazy loading con LazySizes.
<!-- Let's load this in-viewport image normally -->
<img src="image.jpg" alt=".."/>

<!-- Let's lazy-load the rest of these images -->
<img data-src="example.jpg" loading="lazy" alt=".." class="lazyload"/>
<img data-src="data.jpg" loading="lazy" alt=".." class="lazyload"/>
<img data-src="intro.jpg" loading="lazy" alt=".." class="lazyload"/>

<script>
  if ('loading' in HTMLImageElement.prototype) {
      const images = document.querySelectorAll("img.lazyload");
      images.forEach(img => {
          img.src = img.dataset.src;
      });
  } else {
      // Dynamically import the LazySizes library
    let script = document.createElement("script");
    script.async = true;
    script.src =
      "https://cdnjs.cloudflare.com/ajax/libs/lazysizes/4.1.8/lazysizes.min.js";
    document.body.appendChild(script);
  }
</script>

Un’alternativa valida al metodo esposto poco sopra può essere quella realizzata a partire dal metodo dell’importazione dinamica:

<!-- Let's load this in-viewport image normally -->
<img src="image.jpg" alt=".."/>

<!-- Let's lazy-load the rest of these images -->
<img data-src="data.jpg" loading="lazy" alt=".." class="lazyload"/>
<img data-src="example.jpg" loading="lazy" alt=".." class="lazyload"/>
<img data-src="intro.jpg" loading="lazy" alt=".." class="lazyload"/>

<script>
(async () => {
    if ('loading' in HTMLImageElement.prototype) {
        const images = document.querySelectorAll("img.lazyload");
        images.forEach(img => {
            img.src = img.dataset.src;
        });
    } else {
        // Dynamically import the LazySizes library
        const lazySizesLib = await import('/lazysizes.min.js');
        // Initiate LazySizes (reads data-src & class=lazyload)
        lazySizes.init(); // lazySizes works off a global.
    }
})();
</script>

Related Posts

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *

Questo sito usa Akismet per ridurre lo spam. Scopri come i tuoi dati vengono elaborati.