JavaScript loading hasta que las imágenes esten cargadas o descargadas

7 minutos

Esperando a que una imagen esté cargada

Dejar ver como cargan las imágenes de un sitio web no es lo más elegante, sobre todo si queremos dar una buena impresión con una animación o efecto. Por ello quiero enseñar rápidamente con un tutorial, o código en JavaScript, como podemos ocultar las imágenes hasta que estén todas cargadas. Básicamente existirá un elemento que ocultará la página hasta que las imágenes estén listas para ser vistas.

Pulsa Play para ver una demostración.

Existen 3 posibilidades que vamos a tratar:

  1. Definiendo las urls a esperar: Indicaremos las urls de las imágenes que deberemos esperar antes de ocultar el loading. Práctico si estamos usando plugins en JavaScript o solo nos interesa una imágenes concretas, como puede ser las que un usuario se puede encontrar al inicio de la página.
  2. Marcando con una clase las imágenes a esperar: Si las imágenes están presentes en el HTML es interesante especificar cuales son las más críticas añadiendo una clase para marcarlas.
  3. Automático, espera a todas las imágenes: No filtramos, esperamos a que todas las imágenes del documento sean cargadas antes de mostrar la web. Esta forma será la que más haga esperar al visitante, pero nos aseguraremos una experiencia optima sin saltos.

En todos los ejemplos tendremos un HTML con el loading que ocultará lo presente en la web.

<!-- Loading -->
<div class="loading loading--show">Cargando imágenes</div>  

Con su respectivo CSS.

.loading {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: gray;
  color: white;
  justify-content: center;
  align-items: center;
  font-size: 2rem;
  display: none;
}

.loading--show {
  display: flex;
}

Lo puedes diseñar como creas necesario, no afectará al funcionamiento.

No obstante se conciente que la clave está en loading–show. Cuando quitemos esta clase del elemento, desaparecerá porque se aplicará un display: none.

Por disponemos de un sencillo ejemplo que representa una web con un título y 2 imágenes. Se da por hecho que están dentro de una carpeta llamada img.

<h1>Web cargada!!!</h1>
<p><img src="img/imagen-pesada-1.jpg"></p>
<p><img src="img/imagen-pesada-2.jpg"></p>

Opción 1: Definiendo las urls a esperar

La técnica es la siguiente:

  1. Se crearán imágenes ocultas a partir de las rutas.
  2. Cada cierto tiempo, se preguntará si están cargadas.
  3. Cuando todas estén cargadas, se ocultará el loading (quitando la clase loading--show).

Habrá que crear un nuevo elemento HTML para almacenar las imágenes de precarga, por lo que añade donde quieras el siguente HTML.

<div id="preload-images"></div>

El código JavaScript sería el siguiente.

document.addEventListener('DOMContentLoaded', () => {

  // Lista de urls que deseas precargar
  const LIST_IMAGES_PRELOAD = ["img/imagen-pesada-1.jpg", "img/imagen-pesada-2.jpg"];
  // Elemento visual del loading
  const LOADING = document.querySelector('.loading');
  // Obtiene elemento donde serán precargadas las imágenes
  const CONTAINER_IMAGES_PRELOAD = document.querySelector('#preload-images');
  // Tiempo de espera entre revisiones en ms
  const SLEEP_CHECK = 50;

  // Create una imagen por cada elemento de la lista LIST_IMAGES_PRELOAD y la guarda en el elemento CONTAINER_IMAGES_PRELOAD

  function makePreloadImages() {

    LIST_IMAGES_PRELOAD.forEach(urlImg => {
      // Crea la imagen
        const IMG_PRELOAD = document.createElement('img');
        // Añade su ruta
        IMG_PRELOAD.src = urlImg;
        // Oculta para que no se muestre
        IMG_PRELOAD.style = 'display: none';
        // Añade al contenedor
        CONTAINER_IMAGES_PRELOAD.appendChild(IMG_PRELOAD);
    });
  }


  // Herramienta para esperar un tiempo determinado en una función asíncrona

  function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
  }

  // Comprueba de forma recursiva si todas las imágenes se han completado
  // Si todas estan descargadas, quitará la clase 'loading--show' a 'loading' para ocultarlo

  async function checkIfAllImagesCompleted() {

    // Obtiene todas las imágenes sin completar
    const NO_COMPLETES = Array.from(CONTAINER_IMAGES_PRELOAD.querySelectorAll('img')).filter((img) => {
        return !img.complete;
    });

    if (NO_COMPLETES.length !== 0) {
      // Vuelve a iterar si existe alguna sin completar
      await sleep(SLEEP_CHECK);
      return checkIfAllImagesCompleted();
    } else {
      // Oculta el loading
      LOADING.classList.remove('loading--show');
    } 
    return true;
  }


  // Inicia
          
  makePreloadImages();
  checkIfAllImagesCompleted();

});

Lo único que deberás modificar es la siguiente línea.

const LIST_IMAGES_PRELOAD = ["img/imagen-pesada-1.jpg", "img/imagen-pesada-2.jpg"];

Deben contener las urls de las imágenes a esperar a partir del documento HTML.

Ejemplo completo

<!DOCTYPE html>
<html lang="es">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <style>
    .loading {
      position: fixed;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
      background: gray;
      color: white;
      justify-content: center;
      align-items: center;
      font-size: 2rem;
      display: none;
    }

    .loading--show {
      display: flex;
    }
  </style>
  <script>
    document.addEventListener('DOMContentLoaded', () => {

      // Lista de urls que deseas precargar
      const LIST_IMAGES_PRELOAD = ["img/imagen-pesada-1.jpg", "img/imagen-pesada-2.jpg"];
      // Elemento visual del loading
      const LOADING = document.querySelector('.loading');
      // Obtiene elemento donde serán precargadas las imágenes
      const CONTAINER_IMAGES_PRELOAD = document.querySelector('#preload-images');
      // Tiempo de espera entre revisiones en ms
      const SLEEP_CHECK = 50;

      // Create una imagen por cada elemento de la lista LIST_IMAGES_PRELOAD y la guarda en el elemento CONTAINER_IMAGES_PRELOAD

      function makePreloadImages() {

        LIST_IMAGES_PRELOAD.forEach(urlImg => {
          // Crea la imagen
            const IMG_PRELOAD = document.createElement('img');
            // Añade su ruta
            IMG_PRELOAD.src = urlImg;
            // Oculta para que no se muestre
            IMG_PRELOAD.style = 'display: none';
            // Añade al contenedor
            CONTAINER_IMAGES_PRELOAD.appendChild(IMG_PRELOAD);
        });
      }


      // Herramienta para esperar un tiempo determinado en una función asíncrona

      function sleep(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
      }

      // Comprueba de forma recursiva si todas las imágenes se han completado
      // Si todas estan descargadas, quitará la clase 'loading--show' a 'loading' para ocultarlo

      async function checkIfAllImagesCompleted() {

        // Obtiene todas las imágenes sin completar
        const NO_COMPLETES = Array.from(CONTAINER_IMAGES_PRELOAD.querySelectorAll('img')).filter((img) => {
            return !img.complete;
        });

        if (NO_COMPLETES.length !== 0) {
          // Vuelve a iterar si existe alguna sin completar
          await sleep(SLEEP_CHECK);
          return checkIfAllImagesCompleted();
        } else {
          // Oculta el loading
          LOADING.classList.remove('loading--show');
        } 
        return true;
      }


      // Inicia
              
      makePreloadImages();
      checkIfAllImagesCompleted();

    });

  </script>
</head>
<body>
  <!-- Loading -->
  <div class="loading loading--show">Cargando imágenes</div>  
  <!-- Lugar donde se cargarán las imágenes de caché -->
  <div id="preload-images"></div>

  <h1>Web cargada!!!</h1>
  <p><img src="img/imagen-pesada-1.jpg"></p>
  <p><img src="img/imagen-pesada-2.jpg"></p>
  
</body>
</html>

Opción 2: Definiendo con una clase las imágenes a esperar

La técnica es la siguiente:

  1. Se espera todas las imágenes que contengan la clase wait-loading.
  2. Cada cierto tiempo, se preguntará si están imágenes están cargadas.
  3. Cuando todas estén cargadas, se ocultará el loading (quitando la clase loading--show).

No olvides añadir la clase a las imágenes seleccioandas.

<img class="wait-loading" src="img/imagen-pesada-1.jpg">

<img class="wait-loading" src="img/imagen-pesada-2.jpg">

El código JavaScript sería el siguiente.

document.addEventListener('DOMContentLoaded', () => {

  // Lista de urls que deseas precargar
  const LIST_IMAGES_PRELOAD = Array.from(document.querySelectorAll(".wait-loading"));
  // Elemento visual del loading
  const LOADING = document.querySelector('.loading');
  // Tiempo de espera entre revisiones en ms
  const SLEEP_CHECK = 50;

  // Herramienta para esperar un tiempo determinado en una función asíncrona

  function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
  }

  // Comprueba de forma recursiva si todas las imágenes se han completado
  // Si todas estan descargadas, quitará la clase 'loading--show' a 'loading' para ocultarlo

  async function checkIfAllImagesCompleted() {

    // Obtiene todas las imágenes sin completar
    const NO_COMPLETES = LIST_IMAGES_PRELOAD.filter((img) => {
        return !img.complete;
    });

    if (NO_COMPLETES.length !== 0) {
      // Vuelve a iterar si existe alguna sin completar
      await sleep(SLEEP_CHECK);
      return checkIfAllImagesCompleted();
    } else {
      // Oculta el loading
      LOADING.classList.remove('loading--show');
    } 
    return true;
  }


  // Inicia
  
  checkIfAllImagesCompleted();

});

Ejemplo completo

<!DOCTYPE html>
<html lang="es">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <style>
    .loading {
      position: fixed;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
      background: gray;
      color: white;
      justify-content: center;
      align-items: center;
      font-size: 2rem;
      display: none;
    }

    .loading--show {
      display: flex;
    }
  </style>
  <script>
    document.addEventListener('DOMContentLoaded', () => {

      // Lista de urls que deseas precargar
      const LIST_IMAGES_PRELOAD = Array.from(document.querySelectorAll(".wait-loading"));
      // Elemento visual del loading
      const LOADING = document.querySelector('.loading');
      // Tiempo de espera entre revisiones en ms
      const SLEEP_CHECK = 50;

      // Herramienta para esperar un tiempo determinado en una función asíncrona

      function sleep(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
      }

      // Comprueba de forma recursiva si todas las imágenes se han completado
      // Si todas estan descargadas, quitará la clase 'loading--show' a 'loading' para ocultarlo

      async function checkIfAllImagesCompleted() {

        // Obtiene todas las imágenes sin completar
        const NO_COMPLETES = LIST_IMAGES_PRELOAD.filter((img) => {
            return !img.complete;
        });

        if (NO_COMPLETES.length !== 0) {
          // Vuelve a iterar si existe alguna sin completar
          await sleep(SLEEP_CHECK);
          return checkIfAllImagesCompleted();
        } else {
          // Oculta el loading
          LOADING.classList.remove('loading--show');
        } 
        return true;
      }


      // Inicia
      
      checkIfAllImagesCompleted();

    });

  </script>
</head>
<body>

  <!-- Loading -->
  <div class="loading loading--show">Cargando imágenes</div>  

  <!--Contenido -->
  <h1>Web cargada!!!</h1>
  <p><img class="wait-loading" src="img/imagen-pesada-1.jpg"></p>
  <p><img class="wait-loading" src="img/imagen-pesada-2.jpg"></p>
  
</body>
</html>

Opción 3: Automático, espera a todas las imágenes

La técnica es la siguiente:

  1. Se capturan todas las imágenes presentes en el documento.
  2. Cada cierto tiempo, se preguntará si están imágenes están cargadas.
  3. Cuando todas estén cargadas, se ocultará el loading (quitando la clase loading--show).

El código JavaScript sería el siguiente.

document.addEventListener('DOMContentLoaded', () => {

  // Lista de urls que deseas precargar
  const LIST_IMAGES_PRELOAD = Array.from(document.images);
  // Elemento visual del loading
  const LOADING = document.querySelector('.loading');
  // Tiempo de espera entre revisiones en ms
  const SLEEP_CHECK = 50;

  // Herramienta para esperar un tiempo determinado en una función asíncrona

  function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
  }

  // Comprueba de forma recursiva si todas las imágenes se han completado
  // Si todas estan descargadas, quitará la clase 'loading--show' a 'loading' para ocultarlo

  async function checkIfAllImagesCompleted() {

    // Obtiene todas las imágenes sin completar
    const NO_COMPLETES = LIST_IMAGES_PRELOAD.filter((img) => {
        return !img.complete;
    });

    if (NO_COMPLETES.length !== 0) {
      // Vuelve a iterar si existe alguna sin completar
      await sleep(SLEEP_CHECK);
      return checkIfAllImagesCompleted();
    } else {
      // Oculta el loading
      LOADING.classList.remove('loading--show');
    } 
    return true;
  }


  // Inicia
  
  checkIfAllImagesCompleted();

});

Ejemplo completo

<!DOCTYPE html>
<html lang="es">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <style>
    .loading {
      position: fixed;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
      background: gray;
      color: white;
      justify-content: center;
      align-items: center;
      font-size: 2rem;
      display: none;
    }

    .loading--show {
      display: flex;
    }
  </style>
  <script>

    document.addEventListener('DOMContentLoaded', () => {

      // Lista de urls que deseas precargar
      const LIST_IMAGES_PRELOAD = Array.from(document.images);
      // Elemento visual del loading
      const LOADING = document.querySelector('.loading');
      // Tiempo de espera entre revisiones en ms
      const SLEEP_CHECK = 50;

      // Herramienta para esperar un tiempo determinado en una función asíncrona

      function sleep(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
      }

      // Comprueba de forma recursiva si todas las imágenes se han completado
      // Si todas estan descargadas, quitará la clase 'loading--show' a 'loading' para ocultarlo

      async function checkIfAllImagesCompleted() {

        // Obtiene todas las imágenes sin completar
        const NO_COMPLETES = LIST_IMAGES_PRELOAD.filter((img) => {
            return !img.complete;
        });

        if (NO_COMPLETES.length !== 0) {
          // Vuelve a iterar si existe alguna sin completar
          await sleep(SLEEP_CHECK);
          return checkIfAllImagesCompleted();
        } else {
          // Oculta el loading
          LOADING.classList.remove('loading--show');
        } 
        return true;
      }


      // Inicia
      
      checkIfAllImagesCompleted();

    });
  </script>
</head>
<body>

  <!-- Loading -->
  <div class="loading loading--show">Cargando imágenes</div>  

  <!--Contenido -->
  <h1>Web cargada!!!</h1>
  <p><img src="img/imagen-pesada-1.jpg"></p>
  <p><img src="img/imagen-pesada-2.jpg"></p>
  
</body>
</html>

Esta obra está bajo una Licencia Creative Commons Atribución-NoComercial-SinDerivadas 4.0 Internacional.

Atribución/Reconocimiento-NoComercial-SinDerivados 4.0 Internacional

Donación con recompensa

  • 1 café: Respondo a tu duda en los comentarios.
  • 2 cafés: Respondo en menos de 24h a tu comentario.
  • 3 cafés: Todo lo anterior y además te doy las gracias en mis redes.
Comprame un café

Comentarios

{{ comments.length }} comentarios

Nuevo comentario

Nueva replica  {{ formatEllipsisAuthor(replyComment.author) }}

Acepto la política de Protección de Datos.

Escribe el primer comentario

Tal vez también te interese...