Está Vivo!- El impacto ajax.
November 9, 2009
Hace un par de días comencé a notar una cosa. Primero fue con twitter y su nueva funcionalidad (que al parecer por el momento la han deshabilitado) de actualizar el sitio cada vez que hay twitts nuevos. Me encantaba por que solo dejaba la ventana abierta y en cualquier momento el titulo de la pestaña cambiaba para indicar cuantos twitts nuevos había. Es mucho más fácil estar al tanto de twitter (y esclavizase a él) de esa manera sin tener que tener otra app abierta. Después comencé a notarlo con otros sitios. Por ejemplo, http://scobleizer.com/ actualiza su barra lateral cada cierto tiempo. Llegó el momento que me dije a mi mismo, Gush! este sitio está vivo!. No es la primera vez que veo que un sitio se mueve por si solo. He visto cientos de rolling menus, y sitios que de una u otra forma “se mueven”, pero eso no me impresiona. Después de todo, todo eso que se mueve no es más que un loop, algo que alguien programó antes y que no cambia-un .gif programado. Sin embargo, estos sitios están no solo moviéndose, sino auto actualizándose para mostrarte el nuevo contenido y es eso precisamente lo que causa la sensación de WOW!
Como programador que soy, se que todo lo que hay detrás no es más que ajax, pero la verdad aun así me impresionó. Probablemente sea por que es una de las primeras veces que veo ajax empleado de forma usable, amigable y responsable, o quizá solo me agarró desprevenido y me sorprendió. Cualquiera que sea la razón, la verdad que por un momento me hizo sentir que estaba viendo algo vivo, algo que se movía, y me produjo la sensación que te produce ver cuando un recién nacido mueve una mano-simplemente increíble. Posiblemente esté exagerando, después de todo ajax ya está creciendo barba. Sin embargo, es agradable ver que finalmente los desarrolladores están aprendiendo a usarlo de forma benéfica y no solo para impresionar damiselas. Seguro que la web nos depara muchas sorpresas todavía. Apps como wave se abren paso y cada vez la web deja de ser ese papel lleno de formularios que proveen la única forma de interacción. En lo personal no me emociona las animaciones ni los drag and drop. Eso lo veo más bien como simples trucos de feria. Los verdaderos avances en la materia son aquellos que no se notan, que han sido tan bien calculados, planeados y desarrollados que se introducen de forma natural de modo que el usuario nunca se da cuenta que lo está usando y por lo tanto nunca tiene que aprender algo nuevo ni adaptarse aun nuevo método de interacción. Way to go, pero la web es cada vez más excitante y nos exige a notros, lo programadores que de cierta manera somos participes en esa evolución web, cada vez mejores y más responsables metodologías para el uso y la aplicación de las nuevas tecnologías. Ahora solo tenemos dos caminos, sentarnos a esperar más avances en la materia, o se nosotros mismos quienes impulsemos eso avances. Tu cual escoges?
Ajax, creacion y empleo del objeto XMLHttpRequest
October 13, 2009
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.