Lazy Loading

27/04/2016 Tweet
Lazy Loading

La carga de un sitio web que tenga una gran cantidad de imágenes puede ser eterna, cada imagen es una petición HTTP lo que la espera para un usuario se puede hacer muy larga e incluso puede hacer que abandonen la página. ¿Qué tal si sólo hiciésemos la petición de la imagen en el momento de usarla? Aquí entra lo que se llama "Lazy Loading" que consiste en retrasar o inicializar la carga de imágenes hasta el momento de ser usadas, en ese caso, cuando la página haga scroll hasta la imagen en cuestión.

Para controlar esto lo podemos hacer con un básico script de la siguiente manera:

1. Remplazar el atributo "src" de todas las imágenes que queramos que tenga carga diferida por el atributo "data-src".

2. Cada vez que se haga scroll (resize u onload) en la página, se recorren todas las imágenes para comprobar si hay alguna imagen dentro del viewport, es decir, si hay alguna imagen dentro del área de mi pantalla. Esto se hace con la siguiente función (ver response on StackOverflow):

function isElementInViewport (el) {

    var rect = el.getBoundingClientRect();

    return (
        rect.top >= 0 &&
        rect.left >= 0 &&
        rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && /*or $(window).height() */
        rect.right <= (window.innerWidth || document.documentElement.clientWidth) /*or $(window).width() */
    );
}

3. Si la imagen en cuestión va a entrar en el viewport se cambia el atributo "data-src" por su verdadero "src". En este momento la imagen se cargará. El código resultante es:


$(window).on("DOMContentLoaded load resize scroll", function () {;
  var images = $("#main-wrapper img[data-src]");
  // load images that have entered the viewport
  $(images).each(function (index) {
    if (isElementInViewport(this)) {
      $(this).attr("src",$(this).attr("data-src"));
            $(this).removeAttr("data-src");
    }
  })
  // if all the images are loaded, stop calling the handler
  if (images.length == 0) {
    $(window).off("DOMContentLoaded load resize scroll")
  }
})

// source: http://stackoverflow.com/questions/123999/how-to-tell-if-a-dom-element-is-visible-in-the-current-viewport/7557433#7557433
function isElementInViewport (el) {
    var rect = el.getBoundingClientRect();

    return (
        rect.top >= 0 &&
        rect.left >= 0 &&
        rect.bottom <= $(window).height() &&
        rect.right <= $(window).width()
    );
}

Problema

Esta función básica está bien pero nos puede dar problemas de rendimiento si tenemos gran cantidad de imágenes a cargar o son muy pesadas. Hay que tener en cuenta que se está haciendo una comprobación de cada una de ellas cada vez que se carga la página, se hace scroll o se redimensiona la página.

Solución

Afortunadamente hay una gran cantidad de librerías que mejoran este problema además de darnos otras opciones como poder controlar si la imagen se ha cargado correctamente o añadir una animación cuando se está cargando.

Aquí os dejo un enlace dónde podeis ver algunas. Se puede ver el efecto si abrimos el firebug de chrome o firefox, veremos como se van cargando las imágenes a medida que hacemos scroll: Github