El objeto XmlHttpRequest es un objeto JavaScript que nos permite realizar peticiones al servidor web sin la necesidad de recargar la página que estamos visualizando en el navegador. Esto es muy útil a nivel de diseño y de flexivilidad de la aplicación. Podemos recargar datos de modo encubierto, podemos manipularlos y mostrarlos sin que el usuario perciba un cambio de página y sin la molestia de la recarga. De este modo una vez que tenemos el objeto XmlHttpRequest podemos hacer peticiones encubiertas al servidor.
Para ello tenemos toda la riqueja que JavaScript, DOM y XML nos ofrece para poder procesar y manipular la información recibida. Normalnente la petición al servidor de datos se disparra con un determinado evento que llama a una función u objeto JavaScript que manejará la petición y manipulará los datos para integrarlos en el contenido de la página.
El objeto XmlHttpRequest tiene las siguiente propiedades:
Define el estado de la petición. Tiene 5 valores posibles:
Es la respuesta recibida a modo de cadena de caracteres. A veces, cuando la respuesta esperada es muy corta o simple es mas fácil tratarla como una simple cadena que no tratarla como un objeto XML
Es la respuesta recibida en modo de objeto DOM XML. Si se deben manipular muchos valores es mas estructurado y ordenado que no tratarlos como una cadena de caracteres.
Es el código de estatus que el servidor envió. como puede ser el 200 para una respuesta correcta o el 404 para una respuesta página no encontrada
Lo mismo pero en modo texto. OK para el código 200 o bien Not Found para un código 404
Este método cancela la petición en curso… como era de esperar.
Devuelve todas las cabeceras de la respuesta en un array de clave/valor.
Devuelve el valor de una cabecera especifica
Este método prepara el objeto XmlHttpRequest para hacer una llamada al servidor. Metodo puede ser tanto POST, como GET, como PUT. Y la url puede ser, por supuesto absoluta o relativa. Debes saber que no puedes hacer peticiones a un dominio distinto al que estás trabajando por motivos de seguridad. Si quieres hacer peticiones a un dominio distinto deberás hacer algún trabajo extra en el servidor. Si usas el método GET por supuesto puedes poner una url con parametros ex: pagina.php?id=1.
Normalmente se usan sólo method y url.
Este es el método que realmente envía la petición al servidor. El parámetro body puede ser usado para pasar cualquier parametro que quieras si usas el método POST. En ese caso puedes formatear tus parametros como lo harías en un GET pero aquí dentro. Si usas el método GET el body puede ser nulo.
Como puedes esperar este método te permite especificar una cabecera con un valor especifico. Normalmente lo usaras para especificar el contexto de la petición.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Pagina de ejemplo</title>
<script language="JavaScript" src="lib/ConstructorXMLHttpRequest.js"></script>
<script language="JavaScript" type="text/javascript">
// mas adelante veremos una funcion mas completa, de momento a modo esquematico esto ya sirve
function getHTTPObject() {
var xmlhttp;
/*@cc_on
@if (@_jscript_version >= 5)
try {
xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
} catch (e) {
try {
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
} catch (E) { xmlhttp = false; }
}
@else
xmlhttp = false;
@end @*/
if (!xmlhttp && typeof XMLHttpRequest != 'undefined') {
try {
xmlhttp = new XMLHttpRequest();
} catch (e) { xmlhttp = false; }
}
return xmlhttp;
}
function estadoPeticion() //Funcion que se llama cada vez que cambia el estado de peticion.readyState.
{
switch(peticion.readyState) //Segun el estado de la petición devolvemos un Texto.
{
case 0:
document.getElementById('estado').innerHTML = "Sin iniciar";
break;
case 1:
document.getElementById('estado').innerHTML = "Cargando";
break;
case 2:
document.getElementById('estado').innerHTML = "Cargado";
break;
case 3:
document.getElementById('estado').innerHTML = "Interactivo";
break;
case 4:
document.getElementById('estado').innerHTML = "Completado";
//Si ya hemos completado la petición, devolvemos además la información.
document.getElementById('resultado').innerHTML = peticion.responseText;
break;
}
}
function cargar() //Funcion de carga. La que se invoca
{
if(peticion) //Si tenemos el objeto peticion
{
// construyo la url a la que voy a hacer la petición
// llamo al motor Ajax que tengo y le paso como parametro el contenido
// de la casilla de texto txtEjemplo
url="motorAjax.php?palabro="+ document.getElementById('txtEjemplo').value;
peticion.open('GET', url, true); //Abrimos la url, true=forma asíncrona
/* Asignamos la función que se llama cada vez que cambia el estado de peticion.readyState
Y LO HACEMOS ANTES THE HACER EL SEND porque inicia la transmisión.*/
peticion.onreadystatechange = estadoPeticion;
peticion.send(null); //No le enviamos datos a la pagina que abrimos.
}
}
//inicializo las variables
var peticion = null; //Creamos la variable para el objeto xmlhttprequest
peticion = new getHTTPObject();
</script>
</head>
<body>
<!--Cuando ocurra el evento oneclick se llamara la función coger-->
<input type="text" name="txtEjemplo" id="txtEjemplo" value="juantxu" >
<button onclick="cargar()">Coge un documento</button>
</form>
<div id="estado">Estado petición</div> <!--Campo para indicar el estado de la petición-->
<div id="resultado">Sin resultado</div>
</body>
</html>
Tenemos 3 funciones y la inicialización de la variable peticion
El objeto XmlHttpRequest, como pasa con todo en este mundo de los navegadores web, se pide de modo distinto dependiendo del fabricante. Para evitar problemas, es útil usar una función como la que hay a continuación. Que no he hecho yo, sinó que tomé prestada a alguien, aunque ahora no recuerdo su nombre.
function getHTTPObject() {
var xmlhttp;
/*@cc_on
@if (@_jscript_version >= 5)
try {
xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
} catch (e) {
try {
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
} catch (E) { xmlhttp = false; }
}
@else
xmlhttp = false;
@end @*/
if (!xmlhttp && typeof XMLHttpRequest != 'undefined') {
try {
xmlhttp = new XMLHttpRequest();
} catch (e) { xmlhttp = false; }
}
return xmlhttp;
}
Es una función que nos devuelve un objeto XmlHttpRequest que nos permitirá hacer peticiones al servidor. El caso es que, como que la técnica de AJAX es una técnica que depende tambien del cliente. Y se ejecuta, principalmente en el navegador cliente. Pues hay que generar el objeto en función del navegador. El objeto resultante es el mismo, tan sólo cambia la manera de crarlo.
Para el caso de MSIE
@if (@_jscript_version >= 5)
try {
xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
} catch (e) {
try {
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
} catch (E) { xmlhttp = false; }
}
y para el resto de mortales:
xmlhttp = new XMLHttpRequest();
Pero Bueeeno… hasta aquí la parte toca()(). Mas adelante veremos una función mucho mas hermosa y compleja tambien. Pero por el momento, y para ver de que va esto ya es suficiente
Esta función nos servirá para definir lo que se hará cuando el objeto xmlhttp lance el evento onreadystatechange. Esto quiere decir que cuando el estado cambie pues se ejecutará el contenido de esta función. ( ya veremos la asignación mas adelane ).
function pide(){
//-- Definimos a que url vamos a hacer la petición
var url = "motorAjax.php"
//-- Creamos el objeto xmlHttp llamando a la función que tenemos para eso
var xmlHttp = getHTTPObject();
//-- Realizamos la petición
xmlHttp.open("GET", url);
//-- Ahora estamos esperando a recibir la respuesta
//-- para eso tenemos la siguiente función
//-- que llamamos cada vez que ocurre que el estado cambia
xmlHttp.onreadystatechange = function(){
//-- Función anónima a ejecutar cada vez que el estado de la petición cambia
//-- si el estado es 4. Es decir, he recivido todo el documento
if (xmlHttp.readyState == 4) {
//-- si he recivido todo el documento y la respuesta ha sido satisfactoria.
if (xmlHttp.status == 200) {
//-- entonces ya tengo los datos, ya los puedo manipular
var documento = xmlHttp.responseXML.documentElement;
//-- muestro por pantalla moviendome por el documento xml
//-- voy recorriendo el array de elementos "respuesta" hasta que llego al final
for (i = 0; i<documento.getElementsByTagName('respuesta').length; i++){
document.write("<br>"+documento.getElementsByTagName('respuesta')[i].firstChild.data);
}
}else{
//-- si el código de status no es 200 (envío sasisfactorio de una respuesta a la petición por parte del servidor web)
//-- informo al usuario de que ha pasado algo
document.write("ha ocurrido un error");
}
}else{
//-- si todavía no he recibido la respuesta pongo este mensaje para impacientes
document.write("Cargando....<br>");
}
}
//-- Enviamos efectivamente la petición al servidor. Enviamos null porque el cuerpo de la petición es nula. Hacemos un GET
xmlHttp.send(null);
}
Pero la técnica de AJAX es una técnica cliente-servidor por lo que nos falta la otra parte. La parte del servidor. En el servidor podemos tener un…
El servidor recibirá una petición http normal y corriente que interpretará y responderá. El servidor no sabe nada de AJAX simplemente da respuesta a la petición que nosotros le enviamos. Vamos a crear un guión PHP sencillo que responda a una petición (de hecho es tan sencillo que podría ser una página estática)
motorAjax.php
<?php
header("Content-Type: text/xml");
echo "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>";
echo "<documentoXml>";
for ($i = 1; $i <= 10; $i++) {
echo "<respuesta>Espuesta numero". $i ;
echo "</respuesta>";
}
echo "</documentoXml>";
?>
y esta es la página completa que te he estado explicando:
index.html
<html>
<head>
<title>prueba de ajax</title>
<script type="text/javascript">
//-- Esta es la función que crea el objeto xmlhttp
function getHTTPObject() {
var xmlhttp;
/*@cc_on
@if (@_jscript_version >= 5)
try {
xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
} catch (e) {
try {
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
} catch (E) { xmlhttp = false; }
}
@else
xmlhttp = false;
@end @*/
if (!xmlhttp && typeof XMLHttpRequest != 'undefined') {
try {
xmlhttp = new XMLHttpRequest();
} catch (e) { xmlhttp = false; }
}
return xmlhttp;
}
//-- Esta es la función que llamamos al apretra el botón
function pide(){
//-- Definimos a que url vamos a hacer la peticion
var url = "motorAjax.php"
//-- Creamos el objeto xmlHttp llamando a la funcion que tenemos para eso
var xmlHttp = getHTTPObject();
//-- Realizamos la petición
xmlHttp.open("GET", url);
//-- Ahora estamos esperando a recibir la respuesta
//-- para eso tenemos la siguiente función
//-- que llamamos cada vez que ocurre que el estado cambia
xmlHttp.onreadystatechange = function(){
//-- Funci.n anonima a ejecutar cada vez que el estado de la peticion cambia
//-- si el estado es 4. Es decir, he recibido todo el documento
if (xmlHttp.readyState == 4) {
//-- si he recibido todo el documento y la respuesta ha sido satisfactoria.
if (xmlHttp.status == 200) {
//-- entonces ya tengo los datos, ya los puedo manipular
var documento = xmlHttp.responseXML.documentElement;
//-- muestro por pantalla moviéndome por el documento xml
//-- voy recorriendo el array de elementos "respuesta" hasta que llego al final
for (i = 0; i<documento.getElementsByTagName('respuesta').length; i++){
document.write("<br>"+documento.getElementsByTagName('respuesta')[i].firstChild.data);
}
}else{
//-- si el c.digo de status no es 200 (envío satisfactorio de una respuesta a la petición por parte del servidor web)
//-- informo al usuario de que ha pasado algo
document.write("ha ocurrido un error");
}
}
}
xmlHttp.send(null);
}
</script>
</head>
<body>
hola
<form name="frmAjax1">
<input type="button" name = "btnPedir" value="haz click" onClick="javascript:pide()">
</form>
</body>
</html>
y aqui puedes ver el ejemplo funcionando. Puedes comprobar que lo que hace es simplemente recibir el XML y leerlo
En el ejemplo de más abajo es una tabla. Por lo tanto tu tienes esa tabla que vas llenando de contenido en funcion de los datos que vayas recibiendo. Y los datos que recives son… son una respuesta del servidor. Osea, texto, o lo que quieras! Lo más cómodo y práctico de manipular es XML. Datos en xml que son fáciles de manipular. Pero puedes hacer lo que te de la real gana!!!!!
Hasta ahora hemos estado viendo las funciones básicas a desarrollar. Pero a partir de ahora vamos a empezar a estructurar el código. Si siguiéramos poniéndolo todo junto acabaríamos con un tocho infumable. Recuerda que nuestros objetivos como programadores diletantes son:
Para ello crearemos un archivo constructorXMLHttpRequest.js que contenga todo el código relativo a esta funcionalidad.
function ConstructorXMLHttpRequest()
{
if(window.XMLHttpRequest) /*Vemos si el objeto window (la base de la ventana del navegador) posee
el método XMLHttpRequest(Navegadores como Mozilla y Safari). */
{
return new XMLHttpRequest(); //Si lo tiene, crearemos el objeto con este método.
}
else if(window.ActiveXObject) /*Sino tenía el método anterior, debería ser el Internet Exp. un
navegador que emplea objetos ActiveX, lo mismo, miramos si tiene el método de creación. */
{
/*Hay diferentes versiones del objeto, creamos un array, que contiene los diferentes tipos desde la
versión mas reciente, hasta la mas antigua */
var versionesObj = new Array(
'Msxml2.XMLHTTP.5.0',
'Msxml2.XMLHTTP.4.0',
'Msxml2.XMLHTTP.3.0',
'Msxml2.XMLHTTP',
'Microsoft.XMLHTTP');
for (var i = 0; i < versionesObj.length; i++)
{
try
{
/*Intentamos devolver el objeto intentando crear las diferentes versiones se puede intentar crear
uno que no existe y se producirá un error. */
return new ActiveXObject(versionesObj[i]);
}
catch (errorControlado) //Capturamos el error, ya que podría crearse otro objeto.
{
}
}
}
/* Si el navegador llego aquí es porque no posee manera alguna de crear el objeto, emitimos un mensaje
de error. */
throw new Error("No se pudo crear el objeto XMLHttpRequest");
}
Como puedes imaginar, todas las peticiones se van a realizar de la misma manera
Construir un objeto XMLHttpRequest –> Realizar la petición –> Manipular la respuesta
En realidad la única parte interesante y con chica es la del final, la manipulación de la respuesta. El constructor y la petición es siempre lo mismo.
Si ya tenemos un constructor, hagamos tambien una clase que nos realize las peticiones. De ese modo tan sólo tendremos que sobreescribir los métodos que manipulen las respuestas y nos olvidaremos de un buen taco de código.
ClasePeticionAjax.js
/* El objetivo de este fichero es crear la clase objetoAjax (en Javascript a las “clases” se les llama “prototipos”) */
function objetoAjax( )
{
/*Primero necesitamos un objeto XMLHttpRequest que cogeremos del constructor para
que sea compatible con la mayoría de navegadores posible. */
this.objetoRequest = new ConstructorXMLHttpRequest();
}
function peticionAsincrona(url) //Función asignada al método coger del objetoAjax.
{
/*Copiamos el objeto actual, si usamos this dentro de la función que asignemos
a onreadystatechange, no funcionara.*/
var objetoActual = this;
this.objetoRequest.open('GET', url, true); //Preparamos la conexión.
/*Aquí no solo le asignamos el nombre de la función, sino la función completa, así cada vez que
se cree un nuevo objetoAjax se asignara una nueva función. */
this.objetoRequest.onreadystatechange =
function()
{
switch(objetoActual.objetoRequest.readyState)
{
case 1: //Función que se llama cuando se está cargando.
objetoActual.cargando();
break;
case 2: //Función que se llama cuando se a cargado.
objetoActual.cargado();
break;
case 3: //Función que se llama cuando se está en interactivo.
objetoActual.interactivo();
break;
case 4:
/*Función que se llama cuando se completo la transmisión, se le envían 4 parámetros.*/
objetoActual.completado(objetoActual.objetoRequest.status,
objetoActual.objetoRequest.statusText,
objetoActual.objetoRequest.responseText,
objetoActual.objetoRequest.responseXML);
break;
}
}
this.objetoRequest.send(null); //Iniciamos la transmisión de datos.
}
/*Las siguientes funciones las dejo en blanco ya que las redefiniremos según nuestra necesidad
haciéndolas muy sencillas o complejas dentro de la página o omitiendolas sino son necesarias.*/
function objetoRequestCargando() {}
function objetoRequestCargado() {}
function objetoRequestInteractivo() {}
function objetoRequestCompletado(estado, estadoTexto, respuestaTexto, respuestaXML) {}
/* Por último diremos que las funciones que hemos creado, pertenecen al ObjetoAJAX, con prototype,
de esta manera todos los objetoAjax que se creen, lo harán conteniendo estas funciones en ellos*/
//Definimos la función de recoger información.
objetoAjax.prototype.coger = peticionAsincrona ;
//Definimos una serie de funciones que sería posible utilizar y las dejamos en blanco en esta clase.
objetoAjax.prototype.cargando = objetoRequestCargando ;
objetoAjax.prototype.cargado = objetoRequestCargado ;
objetoAjax.prototype.interactivo = objetoRequestInteractivo ;
objetoAjax.prototype.completado = objetoRequestCompletado ;
A continuación os dejo unas cuantas funciones que a mi me son útiles.
( sin ordenar todavia)
JavaScript Document
var enProceso = false;
var http = getHTTPObject(); /
var cuerpo = document.getElementById("cos")
function handleHttpResponse() {
if (http.readyState == 4) {
if (http.status == 200) {
procesaContenido();
document.getElementById("chivato").innerHTML="";
document.getElementById("chivato").className = "visible";
document.getElementById("botoncico").className = "visible";
enProceso = false;
}else{
document.getElementById("chivato").innerHTML="Vuelva a realizar la petición....";
enProceso = false;
}
}else{
document.getElementById("chivato").innerHTML="Cargando....";
document.getElementById("botoncico").className = "oculto";
}
}
function procesaContenido(){
var cuerpo = document.getElementById("cos");
var tabla = document.getElementById("tabla")
var nuevaLinea = document.createElement("tr");
var nuevoCuerpo = document.createElement('tbody');
tabla.replaceChild(nuevoCuerpo,cuerpo);
var vxml = http.responseXML.documentElement;
var totalSuma = 0;
for (i = 0; i< 1){
var nuevaLinea = document.createElement("tr");
var casilla = document.createElement("INPUT");
var nuevaCelda = document.createElement("td");
casilla.type = "checkbox"
casilla.name = i
casilla.value = trim(vxml.getElementsByTagName('id')[i].firstChild.data);
nuevaCelda.appendChild(casilla);
nuevaLinea.appendChild(nuevaCelda);
var expediente = document.createTextNode(trim(vxml.getElementsByTagName('expediente')[i].firstChild.data));
var nuevaCelda = document.createElement("td");
nuevaCelda.appendChild(expediente);
nuevaLinea.appendChild(nuevaCelda);
var billete = document.createTextNode(trim(vxml.getElementsByTagName('id')[i].firstChild.data));
var nuevaCelda = document.createElement("td");
nuevaCelda.appendChild(billete);
nuevaLinea.appendChild(nuevaCelda);
var fecha = document.createTextNode(trim(vxml.getElementsByTagName('fecha')[i].firstChild.data));
var nuevaCelda = document.createElement("td");
nuevaCelda.appendChild(fecha);
nuevaLinea.appendChild(nuevaCelda);
var ruta = document.createTextNode(trim(vxml.getElementsByTagName('ruta')[i].firstChild.data));
var nuevaCelda = document.createElement("td");
nuevaCelda.appendChild(ruta);
nuevaLinea.appendChild(nuevaCelda);
var precio = document.createTextNode(trim(vxml.getElementsByTagName('precio')[i].firstChild.data));
var importe =trim(vxml.getElementsByTagName('precio')[i].firstChild.data)
var nuevaCelda = document.createElement("td");
nuevaCelda.appendChild(precio);
nuevaLinea.appendChild(nuevaCelda);
totalSuma = totalSuma + parseFloat(importe.replace(',','.'));
var pasajero = document.createTextNode(trim(vxml.getElementsByTagName('pasajero')[i].firstChild.data));
var nuevaCelda = document.createElement("td");
nuevaCelda.appendChild(pasajero);
nuevaLinea.appendChild(nuevaCelda);
var pupitre = document.createTextNode(trim(vxml.getElementsByTagName('pupitre')[i].firstChild.data));
var nuevaCelda = document.createElement("td");
nuevaCelda.appendChild(pupitre);
nuevaLinea.appendChild(nuevaCelda);
nuevoCuerpo.appendChild(nuevaLinea);
}
}
tabla.appendChild(nuevoCuerpo);
nuevoCuerpo.id="cos";
document.getElementById("sumatorio").innerHTML="Importe total : "+totalSuma.toFixed(2) + " € ";
}
function trim(str) {
return str.replace(/(^\s+)([^\s]*)(\s+$)/, '$2');
}
function pideAjax(){
var tipo, intr;
var f1 = document.getElementById("sel_1").value + "-"+document.getElementById("mes_salida").value + "-"+ document.getElementById("anyo_salida").value;
var f2 = document.getElementById("sel_2").value + "-"+document.getElementById("mes_regreso").value + "-"+ document.getElementById("anyo_regreso").value ;
if (!Comparar_Fecha(f1 ,f2)){
alert("la fecha DESDE ha de ser mas pequeña que la fecha HASTA");
return false;
}
f1 = document.getElementById("anyo_salida").value + "-"+document.getElementById("mes_salida").value + "-"+ document.getElementById("sel_1").value;
f2 = document.getElementById("anyo_regreso").value + "-"+document.getElementById("mes_regreso").value + "-"+ document.getElementById("sel_2").value;
if(document.getElementById("air1").checked){
tipo = document.getElementById("air1").value;
}
else{
tipo = document.getElementById("air2").value;
}
if(document.getElementById("in1").checked){
intr = document.getElementById("in1").value;
}
else{
intr = document.getElementById("in2").value;
}
var oficina = document.getElementById("oficina").value;
var pupitre = document.getElementById("pupitre").value;
var expediente = document.getElementById("expediente").value;
if (!enProceso && http) {
var url = "motorAjax.asp?t="+ tipo +"&f1="+ f1 + "&f2=" + f2 + "&i=" + intr+ "&o=" + oficina+ "&p=" + pupitre + "&e=" + expediente;
http.open("GET", url, true);
var cuerpo = document.getElementById("cos");
var tabla = document.getElementById("tabla");
var nuevaLinea = document.createElement("tr");
var nuevoCuerpo = document.createElement('tbody');
tabla.replaceChild(nuevoCuerpo,cuerpo);
nuevoCuerpo.id="cos";
http.onreadystatechange = handleHttpResponse;
enProceso = true;
http.send(null);
}
}
function getHTTPObject() {
var xmlhttp;
/*@cc_on
@if (@_jscript_version >= 5)
try {
xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
} catch (e) {
try {
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
} catch (E) { xmlhttp = false; }
}
@else
xmlhttp = false;
@end @*/
if (!xmlhttp && typeof XMLHttpRequest != 'undefined') {
try {
xmlhttp = new XMLHttpRequest();
} catch (e) { xmlhttp = false; }
}
return xmlhttp;
}
var ultimaOrdenacion = 0;
function ordenarTabla(columna){
var cuerpo = document.getElementById("cos");
var tabla = document.getElementById("tabla");
var lineas = cuerpo.getElementsByTagName('tr');
var matrizDeLineas = new Array();
var longitud = lineas.length;
for (var i=0; i bVal){
rVal = 1;
}else{
rVal = -1;
}
return rVal;
}
function cambia(id){
if(document.getElementById(id).style.display=='none'){
document.getElementById(id).style.display='block';
}
else{
document.getElementById(id).style.display='none';
}
}