Ajax, creacion y empleo del objeto XMLHttpRequest

Continuando con la serie sobre ajax, veamos esta ocasión un ejemplo sencillo de la creación y el uso del objeto XMLHttpRequest, o XHR como se le conoce para abreviar. Quiero hacer notar que debido a la creciente actividad anti IE6 no quiero tratar los métodos de creación del objeto XMLHttpRequest en IE6, que generalmente requiere un try… catch. A partir de IE7, XMLHttpRequest es soportado de forma nativa, por lo que nos vamos a quedar con este método directo de acceso al objeto. Si te interesa brindar soporte para ajax en IE6, puedes buscar en la web sobre el tema.

Como una nota aparte, try… catch debería ser evitado por los problemas de desempeño que trae consigo.

Creando una nueva instancia:

Crear una nueva instancia de XHR es sumamente sencillo:


var miXHR = new XMLHttpRequest();

Con esto has creado una nueva instancia de XHR lista para ser usada, exprimida y explotada. Ahora, veamos la belleza de XHR en acción.

Para poder seguir el ejemplo, primero tienes que tener la siguiente preparación:

1)Un folder llamado ajax
2)Dentro del folder, un archivo de texto llamado texto.txt
3)Dentro del folder, un archivo HTML llamado ajax.html
4)Dentro del folder, un archivo js llamado ajax.js

El archivo de texto puede contener cualquier texto que tu quieras. El mio, por ejemplo contiene:

“Javascript doesn’t suck, you are just doing it wrong” Douglas Crockford.

Mi archivo HTML contiene tan solo el HTML necesario para ser considerado válido:


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="es" lang="es">
   <head>
      <meta http-equiv="content-type" content="text/html; charset=utf-8" />
      <title>Ajax! Ah javascript ambiciosamente xtremo!!!</title>
      <script type="text/javascript" src="ajax.js"></script>
   </head>
   <body>
      <p id="ajaxPantalla"></p>
   </body>
</html>

Y mi archivo Javascript esta completamente vacío… por el momento.

Eso es todo lo que necesitas por ahora. Empecemos a trabajar en nuestro javascript:

archivo: ajax.js


window.onload = function(){
   //declaramos un event handler para el evento load de window.
}

Empezamos declarando un event handler para el evento load de window. Este nos va a permitir acceder al DOM cuando ya este totalmente cargado. El DOM, teóricamente, se termina de cargar antes de que el evento load de window sea activado, sin embargo, los métodos para poder detecta cuando el DOM ha sido cargado son, al momento de escribir este articulo, demasiado engorrosos y desde mi punto de vista no vale la pena recurrir a ellos por varias razones.
Nótese que estamos usando event handlres y no event listeners, esto es solo por comodidad, si quiere puedes usar event listeners, aun que en este ejemplo no creo que valga la pena pasar por todo el trabajo extra que eso requiere.

Continuando con nuestro ejemplo, procedamos a crear una instancia del objeto XHR y algunas variables extra que nos serán de utilidad:

archivo: ajax.js
funcion: window.onLoad


var xhr = new XMLHttpRequest();
var pantalla = document.getElementById('ajaxPantalla');
var initTime = new Date().getTime();

Empezamos por definir una nueva instancia de XMLHttpRequest, y la guardamos en una variable (xhr).
También tomamos una referencia a uno de los elementos en nuestro HTML y lo guardamos en otra variable (pantalla)
Por último, guardamos el tiempo actual en otra variable (initTime). Este ultimo paso es solo para poder ir monitorizando el tiempo que toman nuestras llamadas en ser procesadas. Al final capturaremos el tiempo de finalizado de la función y lo compararemos con el tiempo de inicio de la función para ver cuanto tiempo ha tomado el su procesamiento. Para ser mas “exactos” combendría mover la variable initTime justo al inicio, antes de xhr, de modo que el tiempo sea lo primero que se toma al iniciar la función. Sin embargo, yo lo he preferido dejar así.

Continuemos con nuestro ejemplo:

archivo: ajax.js
function: window.onload


if(xhr){
   xhr.onreadystatechange = function(){
   if(xhr.readyState == 4){
      //if(xhr.status == 200 || xhr.state == 304){
         pantalla.innerHTML = xhr.responseText;
      //}
   }else{
      //proveer mensajes para los diferentes codigos http.
   }
}
xhr.open('GET','texto.txt',true);
xhr.send(null);
}else{
   //proveer una alternativa a ajax si no está disponible.
}

Aquí es donde todo esta pasando. Primero chequeamos que efectivamente xhr esté disponible, de no ser así, proveemos un método alternativo:


if(xhr){
   ...
}else{
   //proveer una alternativa a ajax si no esta disponible
}

En este ejemplo no vamos a hablar sobre alternativas, eso lo dejaremos para ejemplos mas adelante.

Si el primer if nos da true, entonces sabemos que xhr esta disponible, por lo que es seguro continuar.


xhr.onreadystatechange = function(){
   if(xhr.readyState == 4){
      //if(xhr.status == 200 || xhr.state == 304){
         pantalla.innerHTML = xhr.responseText;
      //}else{
         //proveer mensajes para los diferentes codigos http.
      //}
   }
}
xhr.open('GET','texto.txt',true);
xhr.send(null);

Lo primero que hacemos una vez determinado que el objeto xhr está disponible, es asignar una función a su evento readystatechange. Este evento se genera cada vez que el estado de la petición cambia. En esta ocasión no hablaremos de los diferentes estados de la petición, eso lo dejaremos para el siguiente artículo. Por ahora basta con que sepas que el cuarto estado es cuando la petición ha sido exitosa y nos ha regresado los resultados.

En un ejemplo real, o si estas corriendo los ejemplos en un localhost, puedes chequear el estado (status) de la respuesta (response). Si sabes algo de los códigos http, sabrás que 200 significa OK y 304 significa no modificado. En nuestro ejemplo chequeamos si el status de la respuesta es 200 o 304, lo que significa que el documento está disponible, de no ser así, enviamos mensajes diferentes dependiendo el código http de la respuesta. En este caso no hemos incluido esos mensajes, pero implementarlos no es difícil.

Una vez determinado que el status de la respuesta es 200 o 304, podemos imprimir el contenido de nuestro archivo txt a nuestra pantalla, para eso usamos innerHTML para asignar a pantalla el texto regresado por xhr.

xhr tiene dos diferentes tipos de respuesta: responseText y responseXML. Dependiendo de lo que estés pidiendo al servidor querrás usar uno u otro. La mayor parte del tiempo estarás usando responseText, por lo que en nuestros ejemplos nos mantendremos especialmente ligados a el.

Las ultimas dos lineas son las que en realidad hacen la petición:

xhr.open() toma mínimo dos parámetros, el método (GET o POST entre otros) y la url del recurso que queremos pedir (en nuestro caso ‘texto.txt’). El tercer parámetro determina si la petición es sincrónica o asíncrona. En nuestro caso, true, le dice al navegador que la petición debe ser asíncrona. En un futuro hablare de las diferencias entre peticiones sincrónicas y asíncronas, por el momento, basta con saber que la mayor parte del tiempo usaras true como tercer parámetro.

Como una nota aparte, el tercer parámetro no debería ser nunca false por problemas de desempeño.

xhr.open() también recibe otros dos parámetros (usuario y clave) para autentificación básica. Estos los usas cuando un sitio te pide una clave y contraseña. Por ejemplo, si vas a:

http://twitter.com/statuses/user_timeline.xml

te darás cuenta que el navegador te pide una clave y contraseña. Si estuvieras haciendo una petición vía ajax a una página similar, usarías los dos últimos parámetros para especificar tu usuario y contraseña. Toma en cuenta que esto no es recomendable ya que ajax es javascript y los archivos javascript pueden ser fácilmente vistos. Su uso puede ser para, por ejemplo, autentificar usuarios usando ajax donde tanto usuario como clave sean provistos por el usuario y no “hard codded” en el javascript.

xhr.send() sirve para enviar datos. En nuestro caso no estamos enviando nada, por lo que usamos null, habrá ocasiones cuando quieras enviar datos, por ejemplo para un sitio que requiere opciones como un índice de temas en un blog. En ejemplos futuros veremos como se usa send() para enviar datos. Por ahora, no es necesario.

Este ha sido un ejemplo sencillo del uso de ajax. En futuros artículos veremos ejemplos mas complejos y útiles. Habrás notado que en este ejemplo todo sucede cuando se carga la pagina, lo cual realmente no tiene sentido. EL objetivo es poder hacer las peticiones como respuesta al input del usuario ya sea hacer click en un enlace, un botón, o simplemente enviar un formulario.

Aquí esta el código completo, notaras que he agregado algunas variables y un par de lineas para medir el tiempo que tarda (en milisegundos) nuestra petición en ejecutarse. Es simplemente para poder ir manteniendo en mente un poco el desempeño de nuestras funciones.

archivo: ajax.js (completo):


window.onload = function(){
   //declaramos un event handler para el evento load de window.
   var xhr = new XMLHttpRequest();
   var pantalla = document.getElementById('ajaxPantalla');
   var initTime = new Date().getTime();
   var finTime = null;
   var totTime = null;

   if(xhr){
      xhr.onreadystatechange = function(){
         if(xhr.readyState == 4){
            //if(xhr.status == 200 || xhr.state == 304){
               pantalla.innerHTML = xhr.responseText;
               finTime = new Date().getTime();
               totTime = finTime - initTime;
               alert(totTime + 'milisegundos');
            //}
         }else{
            //proveer mensajes para los diferentes codigos http.
         }
      }
      xhr.open('GET','texto.txt',true);
      xhr.send(null);
   }else{
      //proveer una alternativa a ajax si no esta disponible.
   }
}

Espero verlos en la siguiente entrega.

8 thoughts on “Ajax, creacion y empleo del objeto XMLHttpRequest

  1. Muy bueno. Una cosa que nunca está demás, también por temas de desempeño, es nulificar el evento onreadystatechange tras recibir la petición. Nulificar o igualar a una función vacía si se trata de explorer 6. Eso y abortar:
    xhr.onreadystatechange=function(){}
    xhr.abort();
    xhr=null;

    • No, pues para ser sincero eso es algo que no conocía. Gracias por el dato, lo tendré en cuenta en posteriores artículos de la serie.

  2. Hola que tal el ejemplo esta muy bueno..
    Tienes por casualidad algun ajax para llenar un combo en JSP desde una consulta a una base de datos access..
    Agradeceria mucho si me ayudaras.
    Por favor.

    saludos.
    Ignacio.

    • Hola, la verdad no tengo scripts para jsp o access. Pero estoy seguro que no es tan dificil de escribir o tal vez puedas encontrar uno por la web.

  3. Existe una alternativa a innerHtml ?

    Desde antes que se desarrollara Ajax, conocí el innerHtml, que a mi parecer debería llamarse innerText, ya que que lo único que hace es poner una cadena en el objeto que le indiquemos. Todo lo que colocamos con innerHtml, aunque se muestra en pantalla, no está realmente en el código por lo que no podemos referenciarlo de forma alguna. Yo sigo usando Iframe por no encontrar otra alternativa.
    Saludos

    • Si, si existe otra manera, puedes usar returnXML para recibir un XML el cual transformas mediante el DOM a HTML y puedes guardar una referencia a cada uno de los elementos importantes, pero no creo que sea necesario. Usando innerHTML puedes hacer referencia al contenido que has cargado mediante el DOM, solo es cuestión de usar algunos ids donde sea necesario. Con respecto a que innerHTML debería se innerText, no estoy de acuerdo. Hay un innerText que solo toma el texto, por ejemplo en:

      hola amigo

      innerHTML te debuelve “hola amigo” con todo y las etiquetas mientras que innerText te devuelve solo “hola amigo” sin las etiquetas.
      Gracias por tu comentario.

Comments are closed.