Concevoir un système de LazyLoading en jQuery

Concevoir un système de LazyLoading en jQuery

Html5, CSS3, Javascript Tutoriels

Le principe du “lazy loading” est simple : faire en sorte que votre navigateur charge uniquement les images qui sont visibles par l’internaute. Il est, en effet, inutile de charger l’ensemble de votre page si votre visiteur ne compte pas parcourir l’intégralité de celle-ci. Et cela est d’autant plus valable si votre page fait 3 kilomètres de long et possède des dizaines d’images.
En mettant en place ce système, vous augmenterez à la fois l’expérience utilisateur (page chargée plus vite) mais vous économiserez également votre bande passante.

Le concept

Pour mettre en place notre système de “lazy loading”, nous allons jouer sur les balises <img>. Ces dernières auront 3 particularités :

  • Par défaut, les images seront des pixels transparent de 1×1 pixel.
  • Ces images possèderont la classe lazy
  • L’adresse de l’image réelle sera stockée dans un attribut data-src

Voilà donc à quoi devront ressembler les images de nos pages :

<img src="img/blank.gif" class="lazy" data-src="img/mon_image.jpg" height="120" width="240" alt="Mon Image" />

La partie Javascript aura pour fonction de parcourir nos balises <img> et de charger l’image réelle si, et seulement si, celle-ci est visible à l’écran.

Le Javascript

La fonction principale de notre plugin se nommera lazyload(). Elle parcourra les images possédant la classe lazy et changera la source par celle présente dans l’attribut data-src.

function lazyload(){
   // on récupère la dimension du scroll vertical de la fenêtre
   var winScrollTop = $(window).scrollTop();

   // on récupère la hauteur de notre fenêtre
   var winHeight = $(window).height();
    
   // on parcourt toutes les images ayant la classe "lazy"
   $('img.lazy').each(function(){

      // on récupère la position verticale de notre image dans la page
      var imgOTop = $(this).offset().top;
      
      // on effectue les modification ssi elle est visible à l'écran
      if( imgOTop < (winHeight + winScrollTop) ) {

         // on change la source de notre image par celle présente dans l'attribut "data-src"
         // on supprime également la classe "lazy" ainsi que l'attribut "data-src" pour éviter de repasser dessus
         $(this)
           .attr( 'src', $(this).data('src') )
           .removeClass('lazy')
           .removeAttr('data-src');

      }
   });
}

Cette fonction devra être exécutée à deux moments :

  • Au chargement de notre page, afin d'afficher les images qui sont immédiatement visibles à l'écran.
  • A chaque fois qu'on effectue un scroll sur notre page.
$(window).scroll(lazyload);
lazyload();

On mixe tout ça dans un plugin jQuery et voici le résultat final :

(function($){
   $.fn.lazyload = function(){
      $(window).scroll(lazyload);
      lazyload();
   }

   function lazyload(){
      var winScrollTop = $(window).scrollTop();
      var winHeight = $(window).height();

      $('img.lazy').each(function(){
         var imgOTop = $(this).offset().top;

         if(imgOTop < (winHeight + winScrollTop)){
            $(this)
               .attr('src', $(this).data('src'))
               .removeClass('lazy')
               .removeAttr('data-src');
         }
      });
   }
})(jQuery);

Ne pas oublier de lancer notre plugin au chargement de notre page :

$(document).ready(function(){
   $(document).lazyload();
});

Et voilà comment, en moins d'une vingtaine de lignes, mettre ne place un système léger et fonctionnel de chargement différé des images.

A propos de l'auteur

Commentaires

  • GeekPress de Référencement WordPress le 07/04/2013 à 10:42

    Vraiment très simple à mettre en place !

    Par contre, ça me pertube un peu au niveau du référencement des images. En gros, le src n'est plus celui de l'image de l'article et Google ne pourra jamais l'indéxé :(

    Je pense que le Lazy Loading est à mettre en place en fonction de la thématique du site. Parce que parfois, Google Image ramène pas mal de visites.

    Et avec une balise noscript, on ne pourrait pas feinter ? Est-ce qu'il chargera tout de même l'image ?

  • Sébastien Decamme le 07/04/2013 à 10:49

    Effectivement, si on tient à référencer correctement les images, cela peut poser problème. Ce système est en général mis sur les portfolios ou les galleries photos volumineuses. Il est possible de "feinter" en jouant sur le display=none des images. En effet, les images non affichées ne sont pas chargées par les navigateurs mais figurent bien dans le DOM. Par contre, Google étant très malin, je ne sais pas s'il référence les images non visibles à l'écran. A tester.

  • TweetPress de API Twitter 1.1 le 07/04/2013 à 13:16

    Bravo, simple et efficace. Je vois bien cela appliqué à nos chers gravatars notamment car ils ont une certaine tendance à peser sur le chargement des pages.

  • GeekPress de Installer WordPress le 07/04/2013 à 14:44

    @TweetPress: Punaise, ton idée est vraiment super bonne. On s'en contre fiche des avatars, surtout qu'ils proviennent d'un domaine externe et qu'ils pourrissent vraiment les perfs d'un site.

    En plus, quand on a parfois 50/60 commentaires, ça commence à faire pas mal de requêtes HTTP...

    J'ai une question pour peut paraître conne : notre image en .gif de 1x1px, si on a 60 images, ça fait 60 requêtes ou qu'une seule ?

  • Sébastien Decamme le 07/04/2013 à 14:56

    @GeekPress : Non il n'y aura qu'une requête effectuée. Et si tu veux t'en débarrasser, un pixel transparent ne vaut pas pas grand chose en base 64 ;)

  • GeekPress le 07/04/2013 à 17:54

    @Seb : J'avais oublié de coup du data URI. Il reste plus qu'à faire la petite astuce WordPress à ce sujet alors :)

  • grosdunord le 07/04/2013 à 18:14

    s'en vouloir être bête on mais sa dans quel fichier ? ^^

  • Sébastien Decamme le 07/04/2013 à 18:19

    @ grosdunord : tu peux mettre le contenu du plugin dans le fichier dans lequel se trouve tes scripts Javascript.
    Quant à l'appel au plugin ($(document).ready...), place-le à la fin de tes documents. Je te conseilles de jetter un oeil à l'article suivant : http://shakup.net/comprendre-et-optimiser-le-chargement-de-vos-scripts/
    Il te donnera quelques précisions sur les appels Javascript.

  • Sébastien Decamme le 07/04/2013 à 18:45

    @grosdunord : tu trouveras un exemple d'utilisation en téléchargeant l'archive suivante : http://shakup.net/demos/lazyload.zip

  • grosdunord le 07/04/2013 à 20:25

    Merci Sébastien je vais regarder sa de plus pret

  • Julio Potier (BoiteAWeb.fr) le 07/04/2013 à 21:22

    Hello Sébastien

    Brillant code, j'adore ça. Par contre je viens de tester ça : "Il est possible de "feinter" en jouant sur le display=none des images. En effet, les images non affichées ne sont pas chargées par les navigateurs mais figurent bien dans le DOM." Et les images se chargent, idem avec visibility:hidden. Et si cela avait marché, alors il aurait suffit que le code JS réaffiche l'image pour la charger et donc pas besoin de data-src ni de base64 1x1, le vrai src aurait été là.
    So, d'où penses-tu que les images "cachées" ne sont pas chargées ?

    Merci !

  • Sébastien Decamme le 07/04/2013 à 21:33

    Bonjour Julio. Effectivement, après test, il s'avère que les images sont bien chargées. Bizarre, j'aurai juré l'inverse. Méa culpa !

  • Julio Potier (BoiteAWeb.fr) le 07/04/2013 à 22:33

    Ca doit surement dépendre du navigateur. Cependant il reste une chose à régler pour moi, c'est le cas du NOJS. Car sans javascript il n'y a aucune image, aie. Et si vous vous dites que Google ne voit pas les images car le JS n'a pas fait son effet, il faut donc prévoir d'afficher l'image dans le cas NOJS.
    Voici mon grain de sel :
    - mes IMG ont la classe "hidden" => <img class="lazy hidden" ...
    - le CSS est : .hidden{ display:none; }
    - le JS a en plus : $('.hidden').show().removeClass('hidden');
    - le markup est : https://gist.github.com/BoiteAWeb/09447b7f0036e0cf66eb

    Si le JS est activé, les images sont montrés et l'effet du lazyload se fait. La balise NOSCRIPT est ignorée
    Si le JS n'est pas activé, les images avec un src de 1x1pixel resteront insivibles mais l'effet de la balise NOSCRIPT arrive et la vraie image s'affiche, comme avant, tout en même temps ;)

    Voilà :

  • GeekPress le 07/04/2013 à 23:57

    Bon, c'est mis en place sur GP. Ca booste bien les perfs avec un nombre de requêtes qui baisse pas mal (en fonction du nombre de commentaire). Merci à vous 3 !

    Je vous fais l'astuce WP dans la semaine sur GP ;)

  • Sébastien Decamme le 08/04/2013 à 7:58

    @Julio Potier : Excellente utilisation de la balise <noscript>. Le problème est ainsi résolu.

  • Page Speed Service, le service d'accélération de Google » Shakup le 01/06/2013 à 16:42

    [...] : minification des fichiers statiques, optimisation des images (.webp) avec réécriture en base64, lazyload, etc… D’ailleurs, si on active toutes les améliorations, on se retrouve avec un nombre [...]

  • Ryum le 26/09/2013 à 1:54

    Bonjour, merci pour cette technique qui marche très facilement.
    Seulement j'ai une petite question sur le script.
    Pourquoi c'est
    $(this)
    .attr('src', $(this).data('src'))
    et non pas
    .attr('src', $(this).data('data-src'))

    Je ne suis pas un pro à l'origine de Jquery mais je ne comprends pas comment fait Jquery pour récupérer l'url contenu dans 'data-src'..
    Merci d'avance pour toute réponse.

  • Sébastien Decamme le 26/09/2013 à 8:32

    @Ryum : En utilisant la méthode ".data()" de jQuery, celui-ci va récuperer automatiquement tous les attributs préfixés par "data-".
    Il n'y a donc pas besoin d'écrire ".data('data-src')" mais uniquement "data('src')".

  • Ryum le 26/09/2013 à 20:18

    D'accord parfait! Merci beaucoup! :)

* KeywordLuv ! Entrez VotreNom@VosMotsClés dans le champ "Nom" pour bénéficier d'un mot-clef ciblé.