Ajax y jQuery 4/6 – Mostrando mensaje de cargando

En el artículo anterior sobre esta serie de Ajax y jQuery vimos como utilizar las librería jQuery para realizar llamadas asíncronas al servidor de tipo GET y POST. Lo que nos quedaría pendiente sería como hacer para mostrar al usuario algún mensaje que indique que una llamada de este tipo se está realizando, ya que si no mostramos nada el usuario no sabrá que algo se está ejecutando. Pasemos entonces a ver como jQuery nos ayuda con esta funcionalidad.

Eventos de jQuery para detectar llamadas Ajax

Existen 6 eventos en jQuery que nos permiten manejar los estados de la conexión asíncrona que realizamos.

  • ajaxStart(): La conexión al servidor se ha abierto. ver más
  • ajaxSend(): La petición GET o POST fue enviada. ver más
  • ajaxSuccess(): La petición volvió sin errores en la comunicación. ver más
  • ajaxError(): La petición obtuvo un error por ejemplo un 404. ver más
  • ajaxComplete(): La petición ha finalizado, independientemente de haberse devuelto un success o error. ver más
  • ajaxStop(): La conexión al servidor se ha cerrado. ver más

Al principio tantos eventos me resultaron un poco confusos, especialmente entre ajaxStart() y ajaxSend(), y ajaxComplete() y ajaxStop() pero sin embargo con el ejemplo que veremos se puede ver bien el funcionamiento.

Para controlar estos eventos, simplemente debemos agregar un listener sobre el objeto que queremos utilizar, ya sea un div con el texto “cargando…” o algún gif animado que muestre alguna animación. Una vez que se les pone a escuchar detectan cualquier llamada Ajax que se ejecute y el ciclo sería el siguiente. Lo primero que ocurre es crear como una conexión al servidor, como un tubo por el cual va a viajar la petición. El ajaxStart() se ejecuta al crearse esa conexión y ajaxStop() al cerrarse. En el medio tenemos el ajaxSend() cuando se envía una petición y el ajaxComplete() cuando la petición vuelve. En el medio de los los últimos dos se pueden ejecutar uno de estos dos eventos ajaxSuccess() o ajaxError().

Tomemos el siguiente código para analizarlo.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
    <head>
        <title></title>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <script type="text/javascript" src="jquery-1.5.min.js"></script>
        <script type="text/javascript">
            jQuery('#test').ready(function(){
 
                jQuery('#log').ajaxStart(function() {
                    jQuery(this).append('ajaxStart<br />');
                });
 
                jQuery('#log').ajaxSend(function() {
                    jQuery(this).append('ajaxSend<br />');
                });
 
                jQuery('#log').ajaxSuccess(function() {
                    jQuery(this).append('ajaxSuccess<br />');
                });
 
                jQuery('#log').ajaxError(function() {
                    jQuery(this).append('ajaxError<br />');
                });
 
                jQuery('#log').ajaxComplete(function() {
                    jQuery(this).append('ajaxComplete<br />');
                });
 
                jQuery('#log').ajaxStop(function() {
                    jQuery(this).append('ajaxStop<br />');
                });
 
                jQuery('#test').load('server.php');
                jQuery('#test').load('no_existe.php');
 
            });
 
        </script>
 
        <style type="text/css">
            #test{
                border: solid #000 1px;
                width:300px;
                height: 350px;
                overflow: auto;
            }
        </style>
 
    </head>
    <body>
 
        <div id="log"></div>
 
        <div id="test">contenido inicial</div>
 
    </body>
</html>
<?php
//-- Un simple sleep para demorar un poco la petición Ajax
sleep(5);
//-- Un texto a devolver. Imaginemos que lo traemos
//   de la base de datos o algo así.
echo 'hello world!';
?>

Tenemos como siempre dos archivos, uno que será el archivo que ejecutará el ajax y otro que actuará como archivo procesador en el servidor. Este último lo único que hará será esperar 5 segundos y escribir un texto que será devuelto para que vean el resultado como si lo estuviese cargando, aunque podría tener cualquier programación necesaria para ejecutar alguna acción u obtener información de una base de datos por ejemplo.

En el archivo test.html, desde la línea 10 a la 32 vemos el registro de los 6 eventos. De esa manera lo que hacemos es registrar cada uno de los eventos vistos más arriba.

El registro de cada evento es muy sencillo. Tomemos el primero para explicarlo.

jQuery('#log').ajaxStart(function() {
    jQuery(this).append('ajaxStart<br />');
});

Como vemos en la página test.html, en la línea 53, vemos un div que utilizaremos en un principio para loguear el momento en el que ocurre el evento. Con la llamada a jQuery(‘#log’) obtenemos la referencia a dicho div y llamamos al método ajaxStart() a quién le pasamos por parámetro una función (callback) que se ejecutará al momento en el que el evento ajaxStart sea llamado. Esta función anónima pasada por parámetro simplemente obtiene la referencia al mismo div (id=log, hacemos referencia a #log con this ya que lo ejecutamos dentro de su contexto) y utiliza el método append, que vimos en el artículo anterior, para agregar el texto al contenido del div. De esta manera, cada evento se encargará de escribir dentro del div el nombre del evento a fin de que veamos los momentos en los que se ejecutan.

Por último, en las líneas 34 y 35, vemos dos llamadas Ajax utilizando el método load() de jQuery visto en el artículo anterior . Esto simplemente ejecuta las peticiones Ajax al servidor para que se ejecuten los eventos registrados en las líneas 10 a la 32. La primera apunta a la página que estamos usando “server.php” mientras que la segunda a una página no existente a fin de provocar un error.

Al ejecutar la página, vemos en el Firebug que se ejecutan dos peticiones Ajax, ambas por el método GET. La segunda retornar una error 404 ya que la página no_existe.php no es encontrada. Al ejecutar la página, vemos en el contenido de nuestro div la siguiente información:

  1. ajaxStart
  2. ajaxSend
  3. ajaxSend
  4. ajaxError
  5. ajaxComplete
  6. ajaxSuccess
  7. ajaxComplete
  8. ajaxStop

Como explicaba más arriba el ajaxStart (1) abre la conexión al servidor. Tanto el evento ajaxStart y ajaxStop son ejecutados una sola vez por más de que existan dos peticiones. Esta conexión es como un tubo por el que viajarán las peticiones y solo será cerrada una vez que TODAS las peticiones fueron finalizadas, por lo que tenemos solo un ajaxStop (8) al final.

A continuación vemos dos ajaxSend (2 y 3), uno por cada petición enviada. Por cada envío de petición (ajaxSend) tendremos una respuesta y un fin. Como vemos al enviar la segunda petición, casi de forma automática el servidor nos responde un error a través del ajaxError (4) ya que la página no existe y como esa petición fue finalizada tenemos un ajaxComplete (5). Esto ocurre mientras la segunda petición sigue ejecutándose y una vez que pasan los 5 segundos, se devuelve la respuesta de la primera petición con un ajaxSuccess (6) y su correspondiente ajaxComplete (7).

Volvamos a hacer un resumen sobre esto.

  1. Lo primero que se ejecutará siempre es un ajaxStart y con esto tendremos conexión al servidor.
  2. Luego se ejecutarán las peticiones
  3. Por cada petición se devolverá una respuesta que podrá ser ajaxSuccess o ajaxError
  4. Independientemente a que la respuesta a la petición haya sido un Success o Error, se ejecuta un ajaxComplete
  5. Una vez que todas las peticiones hayan finalizado se ejecutará un ajaxStop.

Obteniendo mayor información de los eventos

Según vemos en la documentación oficial de jQuery sobre Ajax, vemos que a cada uno de estos eventos que vimos se les pasa como argumento una función que se ejecutará cuando el evento sea invocado. Esta función puede recibir ciertos parámetros opcionales que nos dan mayor información.

  • .ajaxStart( handler() )
  • .ajaxSend( handler(event, XMLHttpRequest, ajaxOptions) )
  • .ajaxSuccess( handler(event, XMLHttpRequest, ajaxOptions) )
  • .ajaxError( handler(event, XMLHttpRequest, ajaxOptions, thrownError) )
  • .ajaxComplete( handler(event, XMLHttpRequest, ajaxOptions) )
  • .ajaxStop( handler() )

El handler es la función anónima que estamos enviando. Tanto el start como stop solo reciben esa función. Los demás eventos pueden recibir la función con parámetros opcionales:

  1. event: El objeto event que nos permitirá tener información del evento ocurrido
  2. XMLHttpRequest: El objeto XMLHttpRequest manejador del Ajax
  3. ajaxOptions: Un objeto que trae información de la petición
  4. thrownError: Texto del error obtenido. Solo para el ajaxError

Podríamos entonces arreglar un poco nuestro código para que sepamos más sobre los errores obtenidos en caso de devolver un ajaxError.

jQuery('#log').ajaxError(function(e, xhr, opts, error) {
    jQuery(this).append('ajaxError<br />');
    alert('La petición a la página ' + opts.url + ' ha devuelto el siguiente error: ' + xhr.status + ' - ' + error);
});

Esto nos devolverá la siguiente información: La petición a la página no_existe.php ha devuelto el siguiente error: 404 – Not Found

Ahora que entendemos los eventos que maneja el jQuery para Ajax pasemos a implementar un mensaje “cargando…”

Implementando el mensaje “cargando…”

Para mostrar el mensaje de carga es realmente sencillo. Podemos usar un texto o un gif animado. En caso de querer usar algún gif animado nos puede ser útil esta página donde nos ayuda a generar algunos íconos.

En realidad mostrar el mensaje es un truco bastante interesante. Lo que hacemos es agregar un div al que pondremos como id loading y por defecto, por medio de CSS, le diremos que debe estar oculto. Cuando se abra una conexión Ajax (ajaxStart) se deberá mostrar el div que estaba oculto y una vez que finalice la conexión (ajaxStop) debemos ocultarlo nuevamente. Por medio del ajaxSuccess podríamos obtener el texto devuelto por la petición Ajax y manejarlo pero no lo utilizaremos porque el método load() de jQuery ya nos carga el texto dentro de nuestro div así que solo usaremos a parte del ajaxStart y ajaxStop el ajaxError para avisar si ocurrió un error.

Modificaremos entonces nuestra página test.html de la siguiente manera.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
    <head>
        <title></title>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <script type="text/javascript" src="jquery-1.5.min.js"></script>
        <script type="text/javascript">
            jQuery('#test').ready(function(){
 
                jQuery('#loading').ajaxStart(function() {
                    jQuery(this).show();
                });
 
                jQuery('#loading').ajaxError(function(e, xhr, opts, error) {
                    alert('La petición a la página ' + opts.url + ' ha devuelto el siguiente error: ' + xhr.status + ' - ' + error);
                });
 
                jQuery('#loading').ajaxStop(function() {
                    jQuery(this).hide();
                });
 
                jQuery('#test').load('server.php');
                jQuery('#test').load('no_existe.php');
 
            });
 
        </script>
 
        <style type="text/css">
            #test{
                border: solid #000 1px;
                width:300px;
                height: 350px;
                overflow: auto;
            }
 
            #loading{
                display: none
            }
        </style>
 
    </head>
    <body>
 
        <div id="loading">cargando...</div>
 
        <div id="test">contenido inicial</div>
 
    </body>
</html>

En la línea 45 vemos el div loading con un texto “cargando…” dentro, y a partir de la línea 37 vemos el CSS necesario para que aparezca como no visible por defecto. Finalmente en las líneas 11 y 19 vemos el evento ajaxStart se encarga de hacer el div visible y el ajaxStop lo hace invisible. Ahora cada vez que ejecutemos una petición ajax y sean ejecutados ambos eventos se mostrará y ocultará el div.

Si queremos que muestre una imágen gif animada el único cambio que haremos será, en lugar del texto “cargando…” dentro del div escribiremos el html necesario para agregar una imagen.

<div id="loading"><img src="loading.gif" /></div>

De esta manera y con un poco de CSS podemos hacer que el div loading sea bastante más interesante y llamativo. Lo ideal es agregar esas líneas JavaScript en el template y olvidarnos del tema.

Como verán jQuery lo tiene todo pensado bastante bien y de una manera bastante sencilla de implementar. Mucha gente piensa que es todo un misterio mostrar el mensaje “cargando” pero es casi transparente para nosotros.

Espero que lo hayan entendido y que aporten con sus comentarios.

Puedes ver el siguiente artículo sobre esta serie aquí.

5 comentarios en “Ajax y jQuery 4/6 – Mostrando mensaje de cargando”

  1. Muchas gracias por tu resumen, me ah sido de muchisima utilidad, eres bien claro en todo lo que transmites, pero tengo una duda… si quisiera ejecutar una peticion dentro de un evneto de estos por ejemplo un ajaxStop, algo asi:

    $(“#capa1”).ajaxStop(function(){
    $(“#capa2”).load(“MostrarMunicipio.php?select=’listProv'”);
    });

    Cuando pruebo esto al parecer se mantiene ejecutandose indefinidamente la peticion de la capa2, como en un ciclo infinito…. Mi objetivo es que inmediatamente que se termine la peticion de la capa1 se ejecute el de la capa2…

    Espero su respuesta muchas gracias de antemano

    1. Lo que ocurre es que los eventos ajaxStart(), ajaxStop(), etc son únicos, no es que tu $(“#capa1″).ajaxStop() detecte el STOP de tu capa1, es por la página entonces cada vez que se ejecuta el ajaxStop() se ejecuta tu método load() que llama a un ajaxStart() y finalmente a su ajaxStop() nuevamente y se ejecuta de nuevo tu load() creando un bucle.

      A mi parecer deberías plantearlo de otra manera. Si comentas un poco más tu idea podemos intentar buscar otra solución 🙂

  2. Muchas gracias por la aportación, un excelente TOPIC, lo buscaba y que mejor que la librería de jQuery haga ya todo el trabajo tedioso.

Comenta este artículo