1. var i = 9898;
2. alert(i);
3. var j = 22;
4. alert(j);
------------- Code end -------------
En este ejemplo el interprete se detiene en la linea 2 mostrando el alert y se pasará
a la linea 3 cuando se cierre el alert y así sucesivamente. Esto es un proceso síncrono.
En el caso de asíncrono el interprete no se pone a la espera de que el proceso termine(se
ejecuta el proceso en background) y pasa a la siguiente línea para su interprete.
XMLHttpRequest no es el único objeto asincrono, también tenemos a la función
setTimeOut() y setInterval().
XMLHttpRequest soporta peticiones síncronas y asíncronas, se usa una conexión asíncrona
cuando la aplicación hará multiples peticiones de forma independiente, pero en el caso
de simplemente tener una petición a la vez, es mucho mas fácil usar una petición sincrona (se
explicará mas adelante).
2. Vida fácil
2.1. Editores
Pueden usar cualquier editor para sus apicaciones dinámicas, algunos se sienten
cómodos usando Ultraedit pero personalmente siento mayor comodidad usando:
Windows:
* Zend Studio
* Komodo IDE Editor
* Aptana IDE
* Scite
Linux:
* Vim ( Es el que yo uso )
* Aptana IDE
* Zend Studio
* Scite
De todos estos editores Aptana y Komodo están orientados a Ajax o Aplicaciones
Ricas de Internet ya que tienes su propio depurador y se acopla perfectamente
con muchos frameworks conocidos como Mootools, Prototype, etc.
2.2. Navegadores
* Mozilla Firefox (http://www.mozilla.org), Navegador recomendado por excelencia
ya que te facilita muchisimo la depuración, edición en caliente(Firebug),
edición de la hoja de estilos(Firebug, Web developer addon), rastreo de
Headers, etc.
* Microsoft Internet Explorer, los depuradores para Internet Explorer son
muy pobres comparados con las herramientas de Mozilla Firefox.
* Opera (http://www.opera.com), excelente navegador con interprete javascript
muy estricto. Úsese para casos de prueba
* Safari (http://www.apple.com/safari), nevagador web usando KHTML para su
interprete estricto. Úsese para casos de prueba.
Nota: Solo existen 4 tipos de navegadores:
1.- Navegadores basados en Gecko de Mozilla (Mozilla firefox, Netscape,
K-meleon, Epiphany).
2.- Microsoft Internet Explorer.
3.- Opera.
4.- Navegadores basados en KHTML (Safari, Konqueror).
Existen muchos navegadores, pero generalmente estan basados en uno de estos
motores para interpretar Html,Javascript,Css.
Con una aplicación compatible en estos 4 motores, oficialmente tienes una
aplicación Crossbrowser.
2.3. Depuradores
No puedes tener una vida tranquila y fácil si no usas un depurador para tus aplicaciones
entre ellas menciono.
Windows:
* Firebug (http://www.getfirebug.com) Extensión de Mozilla Firefox.
* Firebug Minimal (http://www.getfirebug.com) Archivo javascript integrado en
cualquier navegador.
* Web Developer (http://addons.mozilla.org) Extensión de Mozilla Firefox.
* IE Inspector (http://www.ieinspector.com) Integrado en Microsoft Internet
Explorer.
* DebugBar (http://www.debugbar.com) Integrado en Microsoft Internet Explorer.
* Aptana Debugger (http://www.aptana.org) Integrado en Aptana Editor para
Mozilla Firefox.
Linux
* Firebug (http://www.getfirebug.com) Extensión de Mozilla Firefox.
* Web Developer (http://addons.mozilla.org) Extensión de Mozilla Firefox.
* Aptana Debugger (http://www.aptana.org) Integrado en Aptana Editor para
Mozilla Firefox.
2.4. Bibliografía
* Creating Web Pages with Asynchronous JavaScript and XML
By: Edmond Woychowsky
Publisher: Prentice Hall
Pub Date: Agosto 08, 2006
Pages: 432
* Ajax Hacks
By: Bruce W. Perry
Publisher: O'reilly
Pud Date: Marzo 2006
Pages: 438
* Ajax Design Patterns
By: Michael Mahemoff
Publisher: O'Reilly
Pub Date: Junio 2006
Pages: 655
* Ajax for Web Application Developers
By: Kris Hadlock
Publisher: Sams
Pub Date: October 30, 2006
Pages: 288
* Ajax Bible
By: Steven Holzner
Publisher: John Wiley & Sons
Pub Date: 2007
Pages: 716
* Javascript Phrasebook: Essential Code and Commands
By: Christian Wenz
Publisher: Sams
Pub Date: August 28, 2006
Pages: 240
* Javascript Bible, Gold Edition
By: Danny Goodman
Publisher: Hungry Minds, Inc.
Pages: 2177
* Javascript Aplication Cookbook
By: Jerry Brandenbaugh
Publisher: O'Reilly
Pub Date: Septembre 1999
Pages: 476
* DOM Scripting
By: Jeremy Keith
Publisher: Friendsoft
Pages: 369
Por lo menos tienes que leer uno de estos buenísimos libros, recomiendo
(Ajax Hacks, Javascript Bible). Si no alcanzaron a leer alguno de estos, ni sueñen
en tener una aplicación mantenible, escalable y crossbrowser.
3. Objeto XMLHttpRequest
3.1. Introducción
Ya que Ajax es un método y no una tecnología usaremos uno de esos métodos, el cual
es usar el Objeto nativo ( Mozilla/Opera/Safari/Gecko ) y ActiveX ( Microsoft
Internet Explorer ) llamado XMLHttpRequest, mediante el cual puedes hacer peticiones
Asíncronas y Síncronas.
3.2. Instancia
Lamentablemente el Objeto XMLHttpRequest se lo instancia de diferentes formas según
el navegador web, por lo tanto haremos un Objeto XMLHttpRequest Crossbrowser ( que
funcione en todos los navegadores por igual ).
------------- Code begin -------------
var XMLHttpObject = function()
{
try{
var xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
}
catch(e)
{
try
{
var xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
catch(e)
{
var xmlhttp = false;
}
}
return (!xmlhttp && typeof XMLHttpRequest!='undefined')?
new XMLHttpRequest():xmlhttp || new function(){};
}
------------- Code end -------------
Instanciamos al objeto.
------------- Code begin -------------
var rpc = new XMLHttpObject();
------------- Code end -------------
Con esto ya tenemos el objeto para trabajar, ahora detallaré sus métodos y propiedades.
3.3. Métodos
---------------------------------------------------------------------------------------
| Método | Descripción |
|-------------------------------------------------------------------------------------|
| | |
| abort() | Cancela la actual peticion HTTP. |
| | |
|---------------------------------|---------------------------------------------------|
| | |
| getAllResponseHeaders(label) | Retorna en una cadena todas las cabeceras HTTP. |
| | |
|---------------------------------|---------------------------------------------------|
| | |
| getResponseHeader(label) | Retorna un valor específico de las cabeceras HTTP|
| | |
|---------------------------------|---------------------------------------------------|
| | |
| open(method,URL,asynchFlag) | Inicializa la peticion XMLHttp. |
| | |
|---------------------------------|---------------------------------------------------|
| | |
| send(content) | Envia la peticion al servidor esperando su |
| | respuesta. |
| | |
|---------------------------------|---------------------------------------------------|
| | |
| setRequestHeader(label,value) | Añade una cabecera y un valor HTTP al momento de |
| | enviar el contenido. |
---------------------------------------------------------------------------------------
3.4. Propiedades
---------------------------------------------------------------------------------------
| Propiedad | Descripción |
|-------------------------------------------------------------------------------------|
| | |
| status | Contiene el código de estado retornado por la |
| | peticion HTTP. Read-only. |
| | |
| | Lista de estados: |
| | |
| | 200 OK |
| | 201 Created |
| | 204 No Content |
| | 205 Reset Content |
| | 206 Partial Content |
| | 400 Bad Request |
| | 401 Unauthorized |
| | 403 Forbidden |
| | 404 Not Found |
| | 405 Method Not Allowed |
| | 406 Not Acceptable |
| | 407 Proxy Authentication Required |
| | 408 Request Timeout |
| | 411 Length Required |
| | 413 Requested Entity Too Large |
| | 414 Requested URL Too Long |
| | 415 Unsupported Media Type |
| | 500 Internal Server Error |
| | 501 Not Implemented |
| | 502 Bad Gateway |
| | 503 Service Unavailable |
| | 504 Gateway Timeout |
| | 505 HTTP Version Not Supported |
| | |
|---------------------------------|---------------------------------------------------|
| | |
| statusText | Contiene la de estado retornado por la peticion |
| | HTTP. |
| | Por ejemplo: |
| | |
| | 200-> retorna "OK" |
| | 501-> retorna "Not Implemented" |
| | |
| | Read-only. |
|---------------------------------|---------------------------------------------------|
| | |
| readyState | Contiene el estado actual de la peticion HTTP. |
| | |
| | 0 uninitialized |
| | 1 loading |
| | 2 loaded |
| | 3 interactive |
| | 4 complete |
| | |
| | Read-only. |
|---------------------------------|---------------------------------------------------|
| | |
| onreadystatechange | Función a la que se llamara cuando el objeto |
| | cambie de estado (leer readyState). |
| | |
| | Read/Write. |
|---------------------------------|---------------------------------------------------|
| | |
| responseText | Contiene la respuesta del servidor en texto |
| | plano. |
| | |
| | Read-only. |
|---------------------------------|---------------------------------------------------|
| | |
| responseXML | Contiene la respuesta del servidor como documento|
| | XML. |
| | |
| | Read-only |
---------------------------------------------------------------------------------------
4. Código
4.1. Peticiones por método POST (paso a paso)
Para este primer ejemplo haremos una peticion por el método POST a nuestro servidor.
1.- Instanciamos el objeto
------------- Code begin -------------
var XMLHttpObject = function()
{
try{
var xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
}
catch(e)
{
try
{
var xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
catch(e)
{
var xmlhttp = false;
}
}
return (!xmlhttp && typeof XMLHttpRequest!='undefined')?
new XMLHttpRequest():xmlhttp || new function(){};
}
var rpc = new XMLHttpObject();
------------- Code end -------------
2.- Iniciamos la petición definiendo el método ("POST") la url ("ejemplo1_server.php")
y la bandera de petición asincrona (true).
Nota: Todo método POST debe tener el header:
"Content-Type","application/x-www-form-urlencoded"encodeURI() Devuelve una cadena en la que todos
los caracteres no-alfanuméricos excepto -_. han sido reemplazados con un
signo de porcentaje (%) seguido por dos dígitos hexadecimales y los espacios
son codificados como signos de suma (+). Esta es la misma codificación usada
en los datos publicados desde un formulario WWW, es decir, el mismo mecanismo
usado para el tipo de medios application/x-www-form-urlencoded.
Este mecanismo difiere de la codificación RFC1738 (vea encodeURIComponent()) en que,
por razones históricas, los espacios son codificados como signos de suma (+).
Esta función es conveniente cuando se codifica una cadena a ser usada como la
parte de consulta de una URL, como método práctico para pasar variables a la
siguiente página.
3.- Enviamos la peticion al servidor en espera de respuesta.
Dentro del contenido enviaremos variables como cadena(separadas por &).
var1=algovar2=otro
quedando
var1=algo&var2=otro
4.- Definimos la función que se llamará en cada cambio de estado y condicionamos
que si el estado (rpc.readyState) sea igual a 4 mostr emos un alert con la
respuesta del servidor (rpc.responseText).
Con este pequeño código ya hemos tenido un primer acercamiento a este método. Para
entender mejor el código y monitorear las peticiones, pueden activar el depurador
Firebug (explicado mas arriba).
4.2. Peticiones por método GET
Hacer una petición por método GET es igual de simple que el método POST con diferencia
de un par de líneas (se daran cuenta cuales).
ejemplo2.html
------------- Code begin -------------
<html>
<head>
<script type="text/javascript">
var XMLHttpObject = function()
{
try{
var xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
}
catch(e)
{
try
{
var xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
catch(e)
{
var xmlhttp = false;
}
}
return (!xmlhttp && typeof XMLHttpRequest!='undefined')?
new XMLHttpRequest():xmlhttp || new function(){};
}
var rpc = new XMLHttpObject();
rpc.open("GET","ejemplo1_server.php?param1=parametro1¶m2=parametro2¶m3=Hola mundo",true);
rpc.send(null);
rpc.onreadystatechange=function()
{
if(rpc.readyState===4)
{
alert(rpc.responseText);
}
}
</script>
</head>
<body>
</body>
</html>
4.3. Respuesta text/plain
Para procesar la respuesta del servidor como texto plano se debe usar el método responseText(),
de esta manera podemos manipular la respuesta en nuestros objetos DOM o bien recibir
texto plano en formato JSON y manipularlo directamente como datos de aplicación.
En este ejemplo recibiremos texto plano y lo incrustaremos en una parte (un bloque DIV)
de nuestra página HTML.
ejemplo3.html
------------- Code begin -------------
<html>
<head>
<script type="text/javascript">
var XMLHttpObject = function()
{
try{
var xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
}
catch(e)
{
try
{
var xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
catch(e)
{
var xmlhttp = false;
}
}
return (!xmlhttp && typeof XMLHttpRequest!='undefined')?
new XMLHttpRequest():xmlhttp || new function(){};
}
var incrustar=function()
{
var rpc = new XMLHttpObject();
var div = document.getElementById("target");
div.innerHTML="Cargando......";
rpc.open("POST","ejemplo3_server.php",true);
rpc.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
rpc.send('a=1');
rpc.onreadystatechange=function()
{
if(rpc.readyState===4)
{
div.innerHTML = rpc.responseText;
}
}
}
</script>
<style>
body
{
font:normal 11px sans-serif;
}
</style>
</head>
<body>
Vestibulum semper. Nullam non odio. Aliquam quam. Vestibulum semper. Nullam non odio.<br>
Aliquam quam. Vestibulum semper. Nullam non odio. Aliquam quam. Vestibulum semper.<br>
Nullam non odio. Aliquam quam. Vestibulum semper. Nullam non odio. Aliquam quam.<br>
<div id="target" style="border:1px solid #EEE;padding:5px;color:#006699;">
</div>
<a href="#" onclick="incrustar(); return false;">Solicitar datos</a><br>
Vestibulum semper. Nullam non odio. Aliquam quam. Vestibulum semper. Nullam non odio.<br>
Aliquam quam. Vestibulum semper. Nullam non odio. Aliquam quam. Vestibulum semper.<br>
Nullam non odio. Aliquam quam. Vestibulum semper. Nullam non odio. Aliquam quam.<br>
</body>
</html>
------------- Code end -------------
ejemplo3_server.php
------------- Code begin -------------
Vestibulum semper. <b>Nullam non odio</b>. Aliquam quam. Vestibulum semper.
Aliquam quam. Vestibulum semper. <u>nullam non odio. Aliquam quam</u>.
<ol>
<li>Blabla</li>
<li>Blabla</li>
<li>Blabla</li>
<li>Blabla</li>
</ol>
Nullam non odio. Aliquam quam. Vestibulum semper. Nullam non odio. Aliquam quam.
------------- Code end -------------
4.4. Respuesta text/xml
Para procesar la respuesta se debe usar el método responseXML(),para convertirlo en un objeto
XMLDocument pudiendo acceder a sus nodos y atributos con nodo.getElementsByTagName("nodo") y así
de forma recursiva.
* DocumentXML.getElementsByTagName("nodo"), Devuelve un Array de Nodos.
* Nodo[0].firstChild.nodeValue, Devuelve el contenido del Nodo.
* Nodo[0].getAttribute("atributo"), Devuelve el valor de un atributo del
actual Nodo.
* Nodo[0].attributes, Devuelve un Array de atributos del actual Nodo.
* Nodo[0].attributes[0].nodeValue, Devuelve el valor de un atributo en el
indice actual.
Se puede usar Firebug para acceder a todas las propiedades y métodos de los Nodos
de esta forma:
1.- Guardar en una variable global el contenido del Nodo o XMLDocument
window.inspector = Nodo[0] o window.inspector = rpc.responseXML
2.- Abrir la pestaña Script en Firebug y añadir a New watch expression la
cadena window.inspector
3.- Navegar por el arbol DOM que genera Firebug.
Para evitar errores con el objeto XML se debe enviar Headers indicando que se va a
responder con un documento XML.
* <?xml version="1.0" ?> : en caso de ser un documento.xml.
* header('Content-Type: text/xml'); : En el caso de crear el documento XML mediante PHP.
* Enviar cabeceras text/xml de acuerdo a la tecnología usada en el servidor.
También se puede usar Firebug para examinar los nodos como DOM Object.
ejemplo4.html
------------- Code begin -------------
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript">
var XMLHttpObject = function()
{
try{
var xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
}
catch(e)
{
try
{
var xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
catch(e)
{
var xmlhttp = false;
}
}
return (!xmlhttp && typeof XMLHttpRequest!='undefined')?
new XMLHttpRequest():xmlhttp || new function(){};
}
window.onload=function()
{
var rpc = new XMLHttpObject();
var target = document.getElementById("target");
rpc.open("POST","musica.xml",true);
rpc.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
rpc.send();
rpc.onreadystatechange=function()
{
if(rpc.readyState===4)
{
var DocumentXML = rpc.responseXML;
var banda = DocumentXML.getElementsByTagName("banda");
parent.window.ss=banda;
for(var i=0;i<banda.length;i++)
{
target.innerHTML+= banda[i].firstChild.nodeValue+"<br>";
target.innerHTML+= "::Género:"+banda[i].getAttribute("genero")+"<br>";
}
target.innerHTML+="<br><br>------------ Response Text ------------<br>";
target.appendChild(document.createTextNode(rpc.responseText));
}
}
}
</script>
</head>
<body>
<pre id="target">
</pre>
</body>
</html>
4.5. Peticiones síncronas
Para realizar una petición síncrona:
1.- Poner en false el tercer parametro del método open(method,URL,asynchFlag). ¡Importante!
2.- La propiedad onreadystatechange ya no es leída porque en la petición sincrona
no cambia la propiedad readyState. Tomarlo como Nota.
ejemplo5.html
------------- Code begin -------------
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript">
var XMLHttpObject = function()
{
try{
var xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
}
catch(e)
{
try
{
var xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
catch(e)
{
var xmlhttp = false;
}
}
return (!xmlhttp && typeof XMLHttpRequest!='undefined')?
new XMLHttpRequest():xmlhttp || new function(){};
}
var asin = function()
{
var rpc = new XMLHttpObject();
var target = document.getElementById("target");
target.innerHTML="Loading......";
rpc.open("POST","ejemplo1_server.php",false);
rpc.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
rpc.send(encodeURI("param1=parametro1¶m2=parametro2¶m3=Hola mundo"));
target.innerHTML = rpc.responseText;
}
</script>
</head>
<body>
<pre>
<a href="#" onclick="asin(); return false;">Cargar petición síncrona</a>
</pre>
<pre id="target">
</pre>
</body>
</html>