Ajax y jQuery 5/6 – Menú Ajax implementando Hijax

Para continuar con la gente que quiere seguir viendo ejemplos sobre la serie de Ajax y jQuery, les tengo preparado un ejemplo más completo, muy sencillo, implementando peticiones asíncronas en los links de un menú. Por supuesto si hacemos esto vamos a usar la idea de Hijax para asegurarnos de que el sitio funcione con y sin Ajax.

Comencemos sin Ajax

El HTML que usaremos de ejemplo, como así también el CSS será muy básico para no desconcentrarnos del punto inicial del Ajax usado con jQuery. Básicamente se trata de un menú creado con una lista no ordenada (<ul><li>). En primer lugar les dejo los archivos del mini sitio sin absolutamente una línea de JavaScript, completamente funcional. Veamos el ejemplo de la primera página.

<!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">
 
        <link href="style.css" rel="stylesheet" type="text/css" />
 
    </head>
    <body>
 
        <div id="mainmenu">
            <ul id="menu">
                <li><a href="inicio.php">Inicio</a></li>
                <li><a href="acercade.php">Acerca de</a></li>
                <li><a href="contacto.php">Contactos</a></li>
            </ul>
        </div>
 
        <div id="content">
            <h1>Bienvenidos</h1>
            <p>Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?</p>
            <p>Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum</p>
            <p>Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum</p>
        </div>
 
    </body>
</html>

Como se muestra en el código, vemos una lista que será nuestro menú. Contiene tres links a tres páginas, inicio.php, acercade.php y contacto.php. Las tres páginas solo contienen información estática aunque podría sin problema obtener información de la base de datos y hacer cualquier proceso normal. Lo único que tenemos que tener en cuenta para el ejemplo es que la información para cada página está dentro de un div con id=”content”. Esto lo usaremos para obtener la información del servidor.

Hagamos los cambios para que funcione con Ajax

Les dejó aquí la versión del mismo sitio con Ajax para que tengan los archivos. De todas maneras trabajemos con la versión sin Ajax y lleguemos juntos al segundo ejemplo. El HTML con las modificaciones completas sería el siguiente.

<?php sleep(1) ?>
 
<!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">
 
        <link href="style.css" rel="stylesheet" type="text/css" />
 
        <script type="text/javascript" src="jquery-1.5.min.js"></script>
        <script type="text/javascript" src="ajax.js"></script>
 
    </head>
    <body>
 
        <div id="loading"><img alt="Cargando"  src="loading.gif" /></div>
 
        <div id="mainmenu">
            <ul id="menu">
                <li><a href="inicio.php" class="ajax">Inicio</a></li>
                <li><a href="acercade.php" class="ajax">Acerca de</a></li>
                <li><a href="contacto.php" class="ajax">Contactos</a></li>
            </ul>
        </div>
 
        <div id="content">
            <h1>Bienvenidos</h1>
            <p>Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?</p>
            <p>Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum</p>
            <p>Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum</p>
        </div>
 
    </body>
</html>

En la línea 1 agregué un sleep() de PHP pero sólo para retrasar la descarga de la página. De esta manera vemos el mensaje “cargando” cada vez que ejecutamos la llamada Ajax. Una vez que lo terminemos podremos eliminar esta línea de los tres archivos.

En las líneas 11 y 12 incluí dos archivos .js. El primero es la librería del jQuery y el segundo un archivo llamado ajax.js que es en donde programaremos el código JavaScript.

En la línea 17 tenemos el div para mostrar un GIF animado que se muestre cuando las peticiones asíncronas sean ejecutadas. Y en todos los links agregué un class CSS con el nombre Ajax que utilizaremos para obtener los links que queramos que se ejecuten con Ajax. De esta manera lo hacemos más genérico y no programamos cada link sino que lo hacemos de una vez.

Ahora que tenemos los cambios en las páginas inicio.php, acercade.php y contacto.php, hagamos una prueba con el Firebug del Firefox. Abramos la consola de JavaScript y escribamos el siguiente código.

jQuery('#content').load('acercade.php');

Lo que hacemos es usar el método load() de los Elementos jQuery ya visto en el artículo 3 sobre la utilización de GET y POST con Ajax. Este método es medio mágico. Se obtiene la referencia del div content con jQuery('#content') y le decimos que en su contenido cargue la página “acercade.php” pasada como argumento.

El problema que vemos aquí es que carga absolutamente toda la página ya que como venimos viendo en los artículos anteriores, todo lo que se escriba en la página de respuesta será lo que volverá en la respuesta asíncrona. Vemos por ejemplo que se cargan las etiquetas del css, js, el menú se repite, etc. Y nos damos cuenta que de la página completa lo único que nos interesa es el div con id=»content». Es aquí donde vemos la magia del método load() que usamos recien. Si escribimos el siguiente código en el Firebug

jQuery('#content').load('acercade.php #content');

Vemos que agregamos el id del elemento HTML que queremos cargar de la página enviada por parámetro. Con esto decimos, el texto escrito sólo en el elemento con id=»content» de la página acercade.php (usamos el selector CSS # para indicar esto) y sólo eso queremos obtenerlo y modificar el contenido del div content de la página en donde estamos. Si yo quisiera cargar sólo el menú escribiría jQuery('#content').load('acercade.php #mainmenu');. Es potente el jQuery verdad?

Ahora que entendemos que usaremos para obtener el resultado de la petición, pasaremos a escribir el código necesario en el archivo ajax.js.

Escribamos el código genérico

jQuery('.ajax').live('click', function(){
 
    jQuery('#content').load(this.href + ' #content', 'ajax=true');
    return false;
 
});

Primeramente obtenemos todos los elementos que contengan un atributo class .ajax y usamos un método JavaScript llamado live() que nos permite crear dinámicamente eventos sobre el array devuelto por la invocación a jQuery('.ajax'). El método live() recibe como primer argumento el tipo de evento que queremos asignar, para este caso cuando se ejecute un “click” sobre los enlaces. Como segundo argumento recibe una función que se ejecutará al momento que sea llamado el evento pasado como primer argumeto.

Dentro de esta función que es ejecutada con el evento, obtenemos la referencia al div #content que queremos usar, como lo habíamos visto más arriba con la ejecución en el Firebug. Ejecutamos el método load() y usando this.href obtenemos la url del link y lo concatenamos con ‘ #content’ para lograr lo explicado más arriba. Con esto hacemos una función super genérica que nos funcione siempre. De esta manera podríamos incluir nuestro ajax.js directamente en el template y cuando queremos que nuestro menú ejecute una acción con Ajax le agregamos un atributo class=”ajax” a los links.

Es importante recordar que para ejecutar una petición GET pasamos los parámetros como un string, mientras que si lo hacemos como un objeto JSON {ajax: true} se ejecutará una petición de tipo POST. Esto es importante recordar ya que por más que logremos el mismo objetivo, por concepto, si queremos obtener información del servidor se usa GET y para actualizar POST como lo vimos en el artículo Métodos GET vs POST del HTTP.

Por último, le estoy pasando al método load() un segundo argumento que serán los parámetros que quiero enviar al servidor a través de la petición. Para este caso le agrego un ajax=true que podría sernos útil en el código de las páginas procesadoras para saber si estoy recibiendo esa petición por ajax o no. Recuerden que en la página procesadora obtenemos ese valor con $_GET[‘ajax’] ya que es un parámetro GET simple y corriente. Como último paso no olvidemos retornar false para matar la funcionalidad del link y que no se ejecute (línea 4).

Nuestro archivo ajax.js, finamente debería tener a parte de este código lo necesario para mostrar el mensaje «cargando» visto en el artículo anterior. De esta manera quedaría así:

//-- Cuando el DOM este cargado
jQuery(document).ready(function(){
 
    //-- Escucharemos la apertura de una conexión asíncrona y
    //   mostraremos el div con id="loading"
    jQuery('#loading').ajaxStart(function() {
        jQuery(this).show();
    });
 
    //-- En caso de devolver un mensaje deberíamos mostrar información
    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);
    });
 
    //-- Cuando la conexión Ajax haya finalizado, ocultaremos el mensaje
    jQuery('#loading').ajaxStop(function() {
        jQuery(this).hide();
    });
 
    //-- Finalmente el código analizado para las peticiones
    jQuery('.ajax').live('click', function(){
 
        jQuery('#content').load(this.href + ' #content', 'ajax=true');
        return false;
 
    });
 
});

Realmente muy sencillo. Ahora probemos nuestras páginas y veremos en el Firebug como se ejecutan las peticiones Ajax con nuestra minúscula cantidad de código 😛

Para probar la funcionalidad de Hijax, hagamos lo siguiente. Desactivemos el soporte de JavaScript de nuestro Firefox en Editar/Preferencias/Contenido y destildemos la opción “Activar JavaScript”. Veremos que nuestra página dejará de funcionar con Ajax y funcionará como la primera versión como si fuera que no existe nada de JavaScript.

Espero que hayan disfrutado del artículo. Los comentarios son bienvenidos.

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

24 Comments

  1. muy buen tutorial aunque no me explico que es lo que contiene los archivos js

    ojala y pudieras mandarmelos con el evento ajaxStop() porfavor.. gracias.!! directo a favoritos.!!

    1. Julio gracias por el comentario. Te refieres al archivo ajax.js del ejemplo? En el artículo tenemos el link para la descarga del ejemplo y ahí dentro está ese archivo.

      La primera parte es usando el ajaxStart, ajaxError y ajaxStop para mostrar el mensaje y en la última parte tenemos el listener para escuchar los eventos del menú.

      Hazme saber si aclaré tu duda.

  2. Gran aporte…me funciono…pero si meto codigo javascrit dentro del div content no se ejecuta…alguna solución??

    Gracias!!!

    1. La implementación que estamos usando aquí es como una introducción a lo que es Ajax para algo genérico y el método .load() es una de las opciones pero no una de las más poderosas.

      jQuery tiene otras funciones interesantes que pueden ir ayudandote a solucionar estos tipos de problemas más específicos. Por ejemplo también existe el método jQuery.ajax() que es más potente, por ahí podrías intentar implementarlo para estos momentos específicos.

      Aquí por ejemplo si funciona con este script pero haciendo una implementación diferente. Por ahí creando otra función para estos casos y en lugar de class=»ajax», class=»ajax_script».
      jQuery.ajax({
      url: «acercade.php»,
      success: function(datos){
      var ret = jQuery(datos).filter(‘#content’).html().replace(/</g, »);
      jQuery(‘#content’).html(ret);
      }
      });

      Lo que hice ahora fue reemplazar los «<» y «>» teniendo en la página procesadora este script dentro del div:

      &lt ;script language=»javascript» type=»text/javascript»&gt ;
      alert(‘hola’);
      &lt ;/script&gt ;

      Puedes ver un poco más sobre este método en la documentación oficial.

      1. GraciaS por responder Juan…seguire intentandolo, haber si consigo algo con la documentación oficial.

        Gracias y Salu2!!!

  3. Hola Juan

    seria posible con $.ajax conseguir el mismo resultado que explicas en este parrafo:

    «Vemos que agregamos el id del elemento HTML que queremos cargar de la página enviada por parámetro. Con esto decimos, el texto escrito sólo en el elemento con id=»content» de la página acercade.php (usamos el selector CSS # para indicar esto) y sólo eso queremos obtenerlo y modificar el contenido del div content de la página en donde estamos.»

    Gracias.

    1. Yo investigué eso Bernabé pero al parecer no hay una forma automática con el jQuery.ajax() pero justamente por eso use el método jQuery(datos).filter(‘#content’)

      jQuery(datos).filter(‘#content’).html().replace(/</g, ”);

      Es decir, lo que hace es obtener "completa" la página y con el método filter() obtiene un ID específico y lo devuelve como html(). De hecho el método load() hace justamente eso: trae el contenido completo y lo filtra.

      1. Hola Juan

        Decirte que ya solucione mi problema (por fin…XD)…y gracias por tus respuestas.

        Salu2!!!

        jQuery(‘.ajax’).live(‘click’, function(){
        var ruta=this.href;
        jQuery.ajax({
        url: ruta,
        dataType:»html»,
        success: function(datos){
        var content = datos.replace(/script/g,’fix’);
        var cod=jQuery(content).find(‘#contenido’).html().replace(/fix/g,’script’);
        jQuery(‘#contenido’).html(cod);
        }
        });

            return false;
        
        });
        
  4. hola juan,

    en la pagina contacto.php tienes un submit, como podria hacer para q la respuesta me carge en otra pagina pero siempre dentro de inicio.php

    1. Hola Marcelo.

      No creo estar entendiendo bien tu consulta. Si quieres realizar el submit y luego mostrarlo en otra página tendrías que ir guardando los datos enviados para luego mostrarlo donde quieras.

      1. sorry, despues de preguntar me di cuenta q cargas el menu en todas las paginas, en realidad lo q necesitaba era cargar el menu en una sola pagina y recargar las demas paginas en un div y ya lo logre con el load de jquery, pero el problema es cuando una de las paginas recargadas tiene un submit y necesito redireccionar a otra pagina, nose como hacer para que la nueva pagina se recarge dentro del div de la pagina principal.

    2. La verdad que me parece bien que se redireccione nuevamente la página.

      Como explicaba en los artículos, Ajax es una utilidad que tiene que ser usada para cosas específicas y no necesariamente en todos los links y submits. Tiene que ser usado para pequeños requests?

      Siempre después de un envío de formulario es altamente recomendable hacer una redirección a alguna página ya que si no lo haces, al dar F5 se podría duplicar la acción.

  5. Excelente aporte con este articulo.
    Pero tengo una duda; como puedo agregar bootstrap en la pagina que se carga.
    Ejemplo un combo box donde pueda ponerle un select personalizado.

    http://../bootstrap-3.3.6/dist/js/bootstrap.min.js
    http://../bootstrap-select-1.10.0/dist/js/bootstrap-select.js

    Maquina 1…
    Maquina 2…
    Maquina 3…
    Maquina 4…

    1. Gracias por el comentario

      Básicamente si estás usando bootstrap en tu web entonces los imports a los assets (archivos.js y archivos.css) estarían importados ya en tu web. En la página que se carga por ajax solo tendrías que aplicar el HTML correo que pertenece a bootstrap, y como el ajax lo único que hace es incluir el código externo como parte del DOM de la página que lo llama, entonces se aplicarían los estilos directamente.

Deja un comentario