Programación Web Avanzada - Módulo 3 - AJAX
Programación Web Avanzada - Módulo 3 - AJAX
Los textos e imágenes publicados en esta obra están sujetos –excepto que se indique lo contrario– a una licencia de
Reconocimiento-Compartir igual (BY-SA) v.3.0 España de Creative Commons. Se puede modificar la obra, reproducirla, distribuirla
o comunicarla públicamente siempre que se cite el autor y la fuente (FUOC. Fundació per a la Universitat Oberta de Catalunya), y
siempre que la obra derivada quede sujeta a la misma licencia que el material original. La licencia completa se puede consultar en:
http://creativecommons.org/licenses/by-sa/3.0/es/legalcode.ca
CC-BY-SA • PID_00172706 AJAX
Índice
Introducción............................................................................................... 5
Objetivos....................................................................................................... 6
Introducción
Una vez que se conocen las diferentes tecnologías utilizadas por AJAX (HTML,
XML, HTTP, Java-Script, DOM, etc.), es el momento de aprender cómo hay
que utilizarlas para desarrollar aplicaciones web dinámicas.
Objetivos
(1)
En este apartado veremos las diferentes técnicas de comunicación con un ser- HTTP es la sigla de hypertext
1 transfer protocol (‘protocolo de
vidor HTTP . transferencia de hipertexto’).
1.1. Introducción
Ved también
AJAX posibilita la creación de aplicaciones web interactivas mediante
el uso de diferentes técnicas y tecnologías. Por un lado, permite inter- Ved la diferencia entre el mo-
delo tradicional y una apli-
cambiar información con el servidor utilizando peticiones asíncronas, cación web que hace uso de
AJAX en el apartado 2 del mó-
sin que esto influya en la interacción del usuario y sin la necesidad de dulo “Introducción a AJAX” de
navegar hacia otra página. Por otro lado, AJAX también posibilita la ac- esta asignatura.
(2)
Inicialmente, se utilizaron algunas particularidades de los marcos HTML2 para HTML es la sigla de hypertext
markup language (‘lenguaje de
el intercambio de información asíncrona. El uso de estas técnicas se popularizó marcado de hipertexto’).
de tal forma que los navegadores empezaron a integrar un nuevo objeto, que
se llamó XMLHttpRequest, implementado especialmente para esta tarea. XMLHttpRequest
Existen clientes de correo basados en web (también llamados clientes de webmail) que
utilizan AJAX para enviar el texto de un nuevo mensaje de forma periódica mientras se
escribe. El servidor almacenará el mensaje para poder recuperarlo en caso de error y así
continuar con su edición.
(3)
Una vez cargada una página HTML, es posible modificar su contenido de for- DOM es la sigla de document ob-
ject model (‘modelo de objetos del
ma dinámica con el uso de DHTML. Se trata de un conjunto de técnicas com-
documento’ o ‘modelo en obje-
binadas con HTML, Java-Script, CSS y DOM3. Mediante código script, se puede tos para la representación de docu-
mentos’).
acceder a un documento HTML y modificar su estructura con el DOM. Estos
CC-BY-SA • PID_00172706 8 AJAX
Ejemplo 1
El ejemplo 1 consta de una página que, una vez cargada, permite modificar su contenido
al presionar un botón. Esta acción crea dinámicamente una etiqueta <h3> con el texto
“Texto a mostrar”, que se añade al documento actual.
<html>
<head>
<title>Ejemplo DOM 1</title>
<script type="text/javascript">
sContenido = 'Texto a mostrar';
function mostrarTexto(){
//Se crea un elemento <h3>
var elH3 = document.createElement('h3');
//Se crea un nodo de texto con el texto contenido
//en sContenido
var elText = document.createTextNode(sContenido);
//Se añade el nodo de texto al nodo <h3>
elH3.appendChild(elText);
//Se añade el nodo <h3> al cuerpo de esta misma página
document.body.appendChild(elH3);
}
</script>
</head>
<body>
<button onclick="mostrarTexto()">
Haga clic para mostrar el texto
</button>
</body>
</html>
Existe una forma más sencilla de actualizar una página sin tener que crear Propiedad innerHTML
HTML dinámico. Las etiquetas <div> y <span> son agrupaciones lógicas que
Al establecer texto HTML a la
permiten diferenciar partes del documento para facilitar el acceso al mismo propiedad innerHTML, se ac-
y así cambiar su contenido y/o estilo. Ambas disponen de la propiedad in- tualiza el modelo de datos del
documento de forma automá-
nerHTML, que permite establecer el texto HTML de su contenido. tica.
CC-BY-SA • PID_00172706 9 AJAX
Ejemplo 2
<html>
<head>
<title>Ejemplo DOM con una división</title>
<script type="text/javascript">
sContenido = '<h3>Texto a mostrar</h3>';
function mostrarTexto(){
var div = document.getElementById('seccionActualizable');
div.innerHTML = sContenido;
}
</script>
</head>
<body>
<button onclick="mostrarTexto()">
Haz clic para mostrar el texto
</button>
<div id="seccionActualizable"></div>
</body>
</html>
Cada vez que sea necesaria la obtención de nuevos datos, se cambiará la lo- Ocultar un marco
calización del contenido del marco oculto para generar una nueva petición
Para ocultar un marco a la vis-
HTTP. Una vez recibidos los nuevos datos en el marco oculto, éstos se podrán ta del usuario, se debe estable-
tratar y mostrar dentro de marcos visibles. cer el alto y el ancho a 0 pun-
tos. Aun así, algunos navega-
dores antiguos muestran los
bordes del marco.
CC-BY-SA • PID_00172706 10 AJAX
A continuación, explicamos los pasos que se han de llevar a cabo en una co-
municación por medio de marcos ocultos para actualizar parte del documento
actual (dichos pasos pueden verse gráficamente en la figura 1).
1) El usuario realiza una acción sobre un elemento HTML (por ejemplo, selec- Acciones sobre un
cionar un elemento de una lista o pulsar un botón). elemento HTML
5) El servidor procesa y genera los nuevos datos, y los envía de vuelta al nave-
gador. En este caso, el destinatario es el marco oculto.
1) Una función que contiene la lógica necesaria para desencadenar las peti-
ciones HTTP. Cada acción que requiera obtener nueva información llamará a
esta función.
2) Una función para tratar los datos alojados dentro del marco oculto una vez
obtenida la respuesta HTTP.
Para realizar una petición de tipo GET, se debe especificar, en la propiedad lo- Marcos HTML
cation de un marco HTML oculto, el URL con el recurso que se desea obtener.
En HTML, los marcos corres-
Para disponer de marcos ocultos, la aplicación web debe contar con una pági- ponden a la etiqueta <fra-
na que defina los marcos visibles y los marcos ocultos. En los marcos visibles, me>.
A continuación, explicamos cada uno de los pasos de esta técnica con dos
ejemplos.
Ejemplo 3
Al hacer clic sobre el botón, se obtiene un texto del servidor, que se representa en la
página, tal como se muestra en la figura 2.
• index.html:
<html>
<frameset rows="100%,0" frameborder="no">
<frame name="visibleFrame" src="ejemplo1.html" noresize="noresize" />
<frame name="hiddenFrame" src="about:blank" noresize="noresize" />
</frameset>
</html>
• ejemplo1.html:
<html>
<head>
<script type="text/javascript">
function requestNewContent(){
top.frames['hiddenFrame'].location = 'nuevocontenido.html';
}
function updateNewContent(sDataText){
var division = document.getElementById('divUpdatable');
division.innerHTML = sDataText;
}
</script>
<title>Marcos ocultos mediante GET</title>
</head>
<body>
<input type="button" onclick="requestNewContent();"
value="Haga clic para modificar el siguiente texto" />
<div id="divUpdatable">
Contenido inicial
</div>
</body>
</html>
Esta página, que se cargará dentro del marco visible, dispone de la interfaz
de usuario y del motor AJAX: la interfaz está formada por un botón y por un
texto encapsulado en una división a la que se le ha asignado el identificador
divUpdatable. El botón solicitará una nueva página al motor AJAX y el nue-
vo contenido se establecerá dentro de esta división.
CC-BY-SA • PID_00172706 13 AJAX
• nuevocontenido.html
<html>
<head>
<script type="text/javascript">
window.onload =
function()
{
var division = document.getElementById('divNuevoContenido');
top.frames['visibleFrame'].updateNewContent(division.innerHTML);
};
</script>
</head>
<body>
<div id="divNuevoContenido">
<b>Nuevo contenido obtenido asíncronamente</b>
</div>
</body>
</html>
Esta página es la obtenida por el marco oculto. Contiene el texto que Evento onload
se desea mostrar encapsulado en una división para facilitar el acceso a
El evento onload asignado al
él. La función asignada al evento window.onload localiza la división objeto window se ejecutará
divNuevoContenido y pasa el código HTML que contiene la función cuando la página se haya car-
gado por completo en el mar-
updateNewContent(sDataText) del motor AJAX. co oculto.
1)�Petición�del�nuevo�contenido
La petición del nuevo contenido se inicia cuando el usuario hace clic sobre el
botón del marco visible. Éste tiene asignado, en el evento onclick, la función
requestNewContent().
...
CC-BY-SA • PID_00172706 14 AJAX
<body>
<input type="button" onclick="requestNewContent();"
value="Haga clic para modificar el siguiente texto" />
...
function requestNewContent(){
top.frames['hiddenFrame'].location = 'nuevocontenido.html';
}
2)�Obtención�del�nuevo�contenido
window.onload =
function()
{
var division = document.getElementById("divNuevoContenido");
top.frames["visibleFrame"].updateNewContent(division.innerHTML);
};
3)�Actualización�del�texto
function updateNewContent(sDataText){
var division = document.getElementById("divUpdatable");
division.innerHTML = sDataText;
}
CC-BY-SA • PID_00172706 15 AJAX
Este ejemplo (ejemplo 4) consiste en una página HTML con dos listas que
permiten al usuario escoger una provincia y una localidad respectivamente.
Como las localidades dependen de las provincias, será necesario actualizar la
segunda lista cada vez que se seleccione una provincia de la primera.
Ejemplo 4
El servidor HTTP dispone de una base de datos con dos tablas que almacenan el conjunto
de provincias y municipios.
• index.html:
<html>
<frameset rows="100%,0" frameborder="no">
<frame name="visibleFrame" src="ProvincesCities.php" noresize="noresize" />
<frame name="hiddenFrame" src="about:blank" noresize="noresize" />
</frameset>
</html>
• provincesCities.php:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<script type="text/javascript">
function requestCities(){
//Se obtiene el índice de la provincia seleccionada
var index = document.getElementById('selectProvinces').selectedIndex;
//Se obtiene el valor del ítem de la provincia seleccionada
var id = document.getElementById('selectProvinces').options[index].value;
//Se redirecciona el marco oculto a la página getCities.php
//añadiendo el parámetro id al URL
top.frames['hiddenFrame'].location = 'getCities.php?id=' + id;
}
/* Función a la que se llamará desde el marco oculto una vez que se disponga
* de una nueva lista de ciudades. Permite establecer el código HTML
* pasado por parámetro a una división en el marco visible. */
function updateNewContent(sDataText){
var division = document.getElementById('divUpdatable');
division.innerHTML = sDataText;
}
</script>
<title>Marcos ocultos GET</title>
</head>
<body>
<h3>Selecciona una provincia y una localidad:</h3>
<table>
<tr><td>Provincias</td><td>Municipios</td></tr>
<tr><td>
<select id="selectProvinces" size="15" onclick="requestCities()">
<?php
//Se prepara la consulta SQL
$db_nombre = 'ajax';
$link = mysql_connect('localhost','ajax','J&J007')
or die('Could not connect ' . mysql_errno());
mysql_select_db($db_nombre ,$link) or die("Error selecting database.");
$sqlQuery = 'Select id, provincia from provincias';
//Si la consulta ha devuelto algún resultado...
if($result = mysql_query($sqlQuery))
CC-BY-SA • PID_00172706 17 AJAX
{
//Por cada registro devuelto se añade un <option> a la lista de provincias
while($row = mysql_fetch_assoc($result))
{?>
<option value="<?php echo $row["id"]?>">
<?php echo $row["provincia"]?>
</option>
<?php }
}
mysql_close($link);
?>
</select>
</td>
<td>
<div id="divUpdatable"></div>
</td>
</tr>
</table>
</body>
</html>
• getCities.php:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<script type="text/javascript">
Esta página será solicitada para obtener los municipios de una provincia. Eje-
cuta las acciones siguientes:
1)�Carga�inicial�de�provincias
Una vez mostrada esta página, el usuario puede seleccionar una provincia.
2)�Selección�de�una�provincia
3)�Iniciar�la�comunicación�con�el�servidor
function requestCities(){
var index = document.getElementById("selectProvinces").selectedIndex;
var id = document.getElementById("selectProvinces").options[index].value;
top.frames["hiddenFrame"].location = "getCities.php?id=" + id;
}
Las dos primeras líneas obtienen el identificador de provincia a partir del ín-
dice del <option> seleccionado de la lista de provincias. La última establece,
en la propiedad location del marco oculto, el URL compuesto por la pági-
CC-BY-SA • PID_00172706 20 AJAX
4)�Generación�de�página�y�respuesta�del�servidor
...
<div id="divisionCities">
<select size="15">
<?php
$db_nombre = 'ajax';
$idProvincia = $_GET["id"];
$sqlQuery = "Select municipio from municipios Where provincia=\""
. $idProvincia."\"";
$link = mysql_connect('localhost','ajax','J&J007')
or die('Could not connect ' . mysql_errno());
mysql_select_db($db_nombre ,$link)
or die("Error seleccionando la base de datos.");
if($result = mysql_query($sqlQuery))
{
while($row = mysql_fetch_assoc($result))
{?>
<option><?php echo $row["municipio"]?></option>
<?php }
}
mysql_close($link);
?>
</select>
</div>
...
Toda la lista está incluida dentro de una división. Ésta servirá para manipular
dicha lista una vez que esté contenida dentro del marco oculto.
window.onload =
function()
{
var divCities = document.getElementById('divisionCities');
top.frames['visibleFrame'].updateNewContent(divCities.innerHTML);
CC-BY-SA • PID_00172706 21 AJAX
};
5)�Actualización�del�contenido
function updateCities(sDataText){
var divisionCities = document.getElementById("divUpdatableCities");
divisionCities.innerHTML = sDataText;
}
Una vez hecha la llamada a esta función, el navegador muestra la nueva lista
de ciudades pertenecientes a la provincia seleccionada.
En algunos casos, es necesario realizar peticiones POST en lugar de peticiones Ved también
GET. Un ejemplo de ello sería el envío de múltiples parámetros o de parámetros
Ved los métodos HTTP GET y
de tamaño considerable. La longitud del URL en las peticiones está limitada y POST en el módulo “Introduc-
depende de cada navegador, por lo que en estos casos sería inviable el uso de ción a AJAX” de esta asignatu-
ra.
GET. En cambio, POST permite el envío de una gran cantidad de información
dentro del cuerpo de la petición.
Los marcos HTML sólo permiten enviar peticiones GET, por lo que únicamen- Formularios HTML
te serán de utilidad para recibir el contenido. En su lugar, se utilizan los for-
Los formularios pueden con-
mularios HTML. Mediante formularios HTML es posible especificar el método tener HTML normal y elemen-
de la petición. tos especiales llamados contro-
les. Al enviar un formulario, los
controles se adjuntan en la pe-
tición de forma automática.
Las propiedades más importantes que deben definirse en el formulario son las
siguientes:
Marco de destino
• method. Es el método de la petición. Se debe establecer "post".
Si no se establece el marco
oculto como destino de una
petición, la respuesta se reci-
• action. Contendrá el URL con el recurso solicitado. birá en la misma página en la
que resida el formulario, ha-
ciendo que se recargue la pá-
• target. Con esta propiedad estableceremos el marco de destino de la res- gina completa.
puesta.
CC-BY-SA • PID_00172706 22 AJAX
Este ejemplo cuenta con los mismos ficheros que el anterior. Los cambios se
hallan en los ficheros provincesCities.php y getCities.php, y se han
marcado con el comentario nuevo.
• provincesCities.php:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<script type="text/javascript">
/*
* Realiza el envío del formulario al servidor mediante POST
*/
function requestCities(){ //nuevo
document.myForm.submit(); //nuevo
} //nuevo
/* Función a la que se llamará desde el marco oculto una vez que se disponga
* de una nueva lista de ciudades. Permite establecer el código HTML
* pasado por parámetro a una división en el marco visible. */
function updateNewContent(sDataText){
var divisionCities = document.getElementById('divUpdatableCities');
divisionCities.innerHTML = sDataText;
}
</script>
<title>Marcos ocultos POST</title>
</head>
<body>
<h3>Selecciona una provincia y una localidad:</h3>
CC-BY-SA • PID_00172706 23 AJAX
<table>
<tr><td>Provincias</td><td>Municipios</td></tr>
<tr><td>
<form name="myForm" method="post" action="getCities.php"
target="hiddenFrame"> //nuevo
<select name="selectProvinces" size="5" onclick=" requestCities()">
<?php
//Se obtiene la lista de provincias
$db_nombre = 'ajax';
$link = mysql_connect('localhost','ajax','J&J007')
or die('Could not connect ' . mysql_errno());
mysql_select_db($db_nombre ,$link) or die("Error selecting database.");
$sqlQuery = 'Select id, provincia from provincias';
//Si hay resultados...
if($result = mysql_query($sqlQuery))
{
//Cada provincia obtenida se añade a la lista HTML
while($row = mysql_fetch_assoc($result))
{?>
<option value="<?php echo $row["id"]?>">
<?php echo $row["provincia"]?>
</option>
<?php }
}
mysql_close($link);
?>
</select>
</form> //nuevo
</td>
<td><div id="divUpdatableCities"></div></td>
</tr>
</table>
</body>
</html>
function requestCities(){
document.myForm.submit();
CC-BY-SA • PID_00172706 24 AJAX
• getCities.php:
}
mysql_close($link);
?>
</select>
</div>
</body>
</html>
Los únicos cambios realizados sobre este fichero son el tratamiento de pará-
metros. En el ejemplo anterior, se obtenían a partir del URL de la petición. En
este caso, se hallan en el cuerpo de la misma.
<?php
...
$idProvincia = $_POST["selectProvincias"];
...
?>
Al igual que en la técnica anterior, es necesario que la página devuelta al cliente Vector $_POST
contenga una función Java-Script en el evento window.onload de la página
El vector $_POST contiene to-
para forzar la actualización de la parte visible. das las variables pasadas por el
método HTTP POST.
La figura 5 muestra paso a paso cada una de las acciones realizadas en este
ejemplo.
Marcos flotantes
Los marcos flotantes son elementos HTML que, al igual que los marcos
tradicionales, pueden presentar una página en su interior. Sin embargo, Los marcos flotantes se intro-
dujeron en la versión 4.0 de
los marcos flotantes son más versátiles y ofrecen algunas ventajas frente HTML y corresponden a la eti-
queta <iframe>. Pueden si-
a los marcos estáticos. tuarse en cualquier posición
dentro del área de cliente del
navegador y definir esta posi-
ción y su dimensión dinámica-
En la técnica con marcos ocultos, se precisa de una página dividida en marcos mente.
Este nuevo modelo de aplicación no exige una división previa de marcos, sino
que éstos se declaran en el mismo documento que requiera el uso de AJAX.
Los marcos flotantes también pueden declararse de forma dinámica mediante
el DOM sin que se precise una definición previa.
CC-BY-SA • PID_00172706 27 AJAX
Los pasos realizados desde que el usuario lleva a cabo una acción hasta que se
actualiza el documento son los siguientes:
6) Una vez recibida la página dentro del marco flotante, éste notifica este su-
ceso al motor AJAX enviando el nuevo contenido.
Ejemplo 6
• index.html:
<html>
<head>
<title>Alta de socio</title>
<script type="text/javascript">
function newUser(){
document.myForm.submit();
}
function showResult(sResult){
var div = document.getElementById('divForm');
div.innerHTML = sResult;
}
</script>
</head>
<body>
<h3>Formulario de inscripción</h3>
<iframe name="hiddenFrame" width="0" height="0" frameborder="0"></iframe>
<div id="divForm">
<form name="myForm" method="post" action="saveData.php" target="hiddenFrame">
<table>
<tr><td>Email</td><td><input name="email" width="20" /></td></tr>
<tr><td>Nombre</td><td><input name="name"/></td></tr>
<tr><td>Apellidos</td><td><input name="surname"/></td></tr>
<tr><td>Dirección</td><td><input name="address"/></td></tr>
<tr><td>Población</td><td><input name="city"/></td></tr>
<tr><td>CP</td><td><input name="postCode"/></td></tr>
<tr><td>Teléfono</td><td><input name="phone"/></td></tr>
<tr><td rowspan="3"><button onclick="newUser()">Enviar</button>
</td></tr>
</table>
</form>
</div>
</body>
</html>
En este documento reside el motor AJAX con dos funciones Java-Script: ne-
wUser(), para realizar la petición, y showResult(sResult), para la visuali-
zación del nuevo contenido. La división divForm que engloba el formulario
se utilizará para mostrar el resultado de la operación sustituyendo el formula-
rio por la respuesta del servidor. También se declara el marco flotante con el
nombre "hiddenFrame", encargado de la comunicación asíncrona.
CC-BY-SA • PID_00172706 29 AJAX
• saveData.php:
<html>
<head>
<script type="text/javascript">
window.onload =
function()
{
var div = document.getElementById("divResponse");
parent.showResult(div.innerHTML);
};
</script>
</head>
<body>
<div id="divResponse">
<?php
$db_nombre = 'ajax';
$sEmail = $_POST["email"];
$sName = $_POST["name"];
$sSurname = $_POST["surname"];
$sAddress = $_POST["address"];
$sCity = $_POST["city"];
$sPC = $_POST["postCode"];
$sPhone = $_POST["phone"];
$sqlQuery = "insert into members"
. " (Email,Name,Surnames,Address,City,PC,Phone) Values ('$sEmail',"
. " '$sName','$sSurname','$sAddress', '$sCity','$sPC','$sPhone')";
$link = mysql_connect('localhost','ajax','J&J007')
or die('Could not connect ' . mysql_errno());
mysql_select_db($db_nombre ,$link)
or die("Error seleccionando la base de datos.");
if($oResult = mysql_query($sqlQuery))
{
$sResult = "Usuario registrado con el identificador: " . mysql_insert_id();
}
else
{
$sResult = "No se ha podido registrar el usuario";
}
echo $sResult;
mysql_close($link);
?>
</div>
</body>
</html>
CC-BY-SA • PID_00172706 30 AJAX
Este documento contiene el script PHP para llevar a cabo el alta del usuario
en la base de datos. Todos los campos que se registran se obtienen de los pa-
rámetros del cuerpo de la petición, a la que se accede desde el vector $_POST.
El documento también dispone de una función Java-Script asignada al evento
window.onload para notificar al motor AJAX la recepción de este contenido
en el marco flotante oculto.
Los cambios más significativos en relación con la técnica de los marcos ocultos
son dos. El primero es la definición del marco oculto. El marco oculto flotante
puede declararse en el mismo documento en el que residen la interfaz y el
motor AJAX.
parent.frames["visibleFrame"].javaScriptFunction();
parent.showResult(div.innerHTML);
1.4. XMLHttpRequest
Aunque el término AJAX no fue introducido hasta el año 2005, anteriormente XMLHttpRequest
ya se hacía uso de las técnicas de marcos ocultos para obtener información
XMLHttpRequest fue ideado
del servidor de forma asíncrona. Paralelamente, los navegadores empezaron a inicialmente por Microsoft y se
incorporar un objeto llamado XMLHttpRequest que permitía realizar las mis- incluyó en su navegador Inter-
net Explorer 5.
mas tareas de una manera más eficiente.
(4)
XMLHttpRequest es una interfaz a la que se accede desde Java-Script que está XML es la sigla de extensible mar-
kup language (‘lenguaje de marca-
implementada por la mayoría de los navegadores, y que permite realizar pe-
do extensible’).
ticiones HTTP. Dispone de un conjunto de mecanismos para el envío y la re-
cepción de peticiones síncronas y asíncronas. El objeto XMLHttpRequest fue (5)
JSON es la sigla de Java-Script
4 object notation.
ideado para intercambiar información en formato XML , pero actualmente
soporta diferentes formatos, como texto ASCII, HTML y JSON5.
Navegadores y XML
La interfaz XMLHttpRequest es regulada por el W3C con especificaciones de- La mayoría de los navegado-
res actuales ofrecen una imple-
finidas en dos niveles: mentación nativa de este ob-
jeto. Es el caso de Mozilla Fire-
fox, Opera, Google Chrome,
1)�XMLHttpRequest�Level�1. Su primer borrador fue presentado en abril del Konkeror, Safari y Microsoft In-
ternet Explorer a partir de su
2006 con la especificación estándar. versión 7.
CC-BY-SA • PID_00172706 31 AJAX
(6)
Al no existir una definición definitiva, los desarrolladores deben tener en cuen- En inglés, frameworks.
ta las diferentes implementaciones de esta interfaz en cada navegador. Estas
diferencias pueden ser minimizadas utilizando entornos de desarrollo6 que Entornos de desarrollo
ofrezcan una capa de abstracción de las diferentes implementaciones. Existen entornos de desarro-
llo (frameworks), usualmente fi-
cheros Java-Script, que se eje-
1.4.1. Propiedades cutan en el lado del cliente y
que facilitan el desarrollo web
ampliando el DOM, ofreciendo
La siguiente tabla muestra las propiedades de XMLHttpRequest: funcionalidades AJAX, etc.
• readyState:
1 OPENED Indica que el objeto está inicializado y preparado para enviar una petición.
2 HEADERS_RECEIVED Todas las cabeceras de la respuesta ya han sido recibidas y algunos miembros del objeto ya es-
tán disponibles.
• responseText:
• responseXML:
• responseBody:
(7)
Proporciona la respuesta en una matriz7 de bytes sin signo. Puede ser útil para En inglés, array.
• status:
1.4.2. Métodos
Método Descripción
getAllResponseHeaders Obtiene todas las cabeceras HTTP de la última petición en una ca-
dena de caracteres.
• send([body]):
• abort():
• setRequestHeader(header, value):
(8)
Toda petición HTTP incorpora un conjunto de líneas opcionales que aportan En inglés, headers.
información adicional al destinatario, sea el cliente o el servidor. Estas líneas se
denominan cabeceras8 y dotan al protocolo HTTP de una gran flexibilidad pa-
CC-BY-SA • PID_00172706 34 AJAX
ra intercambiar datos sobre la transacción: información sobre el tipo de conte- Ved también
nido, hora y fecha de la petición, información sobre el navegador del cliente,
Para más información sobre el
etc. Las cabeceras están compuestas por un nombre, seguido de dos puntos protocolo HTTP, ved el aparta-
(:) y el valor. do 2.1 del módulo "Introduc-
ción a AJAX" de esta asignatu-
ra.
Esta cabecera indica el tipo de contenido, que en este caso es una página
HTML, y su correspondiente codificación de caracteres.
• getAllResponseHeaders():
• getResponseHeader(header):
1.4.3. Eventos
La tabla siguiente muestra los eventos que permiten notificar los cambios de
estado y los sucesos que ocurren en una transacción HTTP:
onreadystatechange ... la propiedad readyState cambia de valor. Todos los eventos fueron in-
troducidos en la especificación
onloadstart ... la petición se inicia. XMLHttpRequest�Level�2, sal-
vo onreadystatechange,
que se definió en la primera
onprogress ... mientras se cargan y se envían datos en el transcurso de un diálogo. especificación.
Para realizar una petición asíncrona con el servidor utilizando la clase XMLHtt-
pRequest, se deben llevar a cabo los pasos siguientes:
2) Inicialización.
5) Envío de la petición.
Ejemplo 7
function createXMLHttpRequestObject() {
/*Esta condición determina si el navegador tiene soporte
nativo para XMLHttpRequest*/
if(window.XMLHttpRequest){
return new XMLHttpRequest();
}
/*No hay soporte nativo, se intenta obtener como un objeto ActiveX
para versiones anteriores de Internet Explorer 7*/
else if(window.ActiveXObject){
try{
return new ActiveXObject('Msxml2.XMLHTTP');
}
catch(e){
try{
return new ActiveXObject('Microsoft.XMLHTTP');
}
catch(E){}
}
}
alert('No se ha podido crear una instancia de XMLHttpRequest');
}
Inicialización
Opcionalmente, puede especificarse un tercer parámetro booleano que indi- Tercer parámetro
que si se desea una comunicación asíncrona o síncrona. El cuarto y el quinto
Si se establece false en el tercer
parámetros, también opcionales, permiten establecer el nombre de usuario y parámetro, el método send
la contraseña en caso de que el servidor HTTP requiera autenticación. bloquea la ejecución hasta que
finaliza la transacción.
Una vez que se ha completado con éxito, la siguiente función permite gestio-
nar una respuesta.
oXMLHttpObject.onreadystatechange = function(){
if(oXMLHttpObject.readyState == 4){
if(oXMLHttpObject.status == 200){
//gestión de la respuesta
}
else{
//error
}
}
}
Para saber si los datos han sido recibidos, la función comprueba el valor de la
propiedad readyState. Si su valor es 4 (DONE), ello implicará que la transac-
ción ha finalizado. Para saber si ha tenido éxito, la función consulta en una
segunda condición si el código del estado de la respuesta es 200 (OK). En ese
caso se realizará la gestión del contenido.
oXMLHttpObject.onload = function(){
if(oXMLHttpObject.status == 200){
//gestión de la respuesta
}
};
oXMLHttpObject.onerror = function(){
alert("XMLHttpRequest: Error en la petición HTTP.");
};
oXMLHttp.setRequestHeader('User-Agent', 'AJAX');
Envío de la petición
oXMLHttpObject.send(null);
Los manejadores de eventos gestionarán la respuesta una vez enviada la peti- Manejadores de eventos
ción. En el caso de que la recepción se haya completado con éxito, se puede
Los manejadores de eventos
acceder a las propiedades responseText o responseXML para acceder así a son las funciones Java-Script
los datos recibidos. asignadas a los diferentes
eventos. Éstos se establecieron
en el paso 3.
Ejemplo mediante peticiones GET
• index.html:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Search member form</title>
<script type="text/javascript" src="../script/Ajax.js"></script>
<script type="text/javascript">
function searchMember(){
//Se obtiene el objeto XMLHttpRequest
var oXMLHttpRequest = createXMLHttpRequestObject();
//Se obtiene el identificador del usuario
var id = document.getElementById("memberNumber").value;
var div = document.getElementById("divRequest");
CC-BY-SA • PID_00172706 39 AJAX
• getMember.php:
<?php
header('Content-Type: text/html; charset=ISO-8859-1');
$db_nombre = 'ajax';
//Se obtiene el identificador del usuario del parámetro en el URL
$idMember = $_GET["id"];
$sqlQuery = "Select * from members Where nMember=\"" .
$idMember."\"";
$link = mysql_connect('localhost','ajax','J&J007') or
die('Could not connect ' . mysql_errno());
mysql_select_db($db_nombre ,$link) or die("Error selecting db.");
//Si hay resultados en la consulta SQL...
CC-BY-SA • PID_00172706 40 AJAX
if($result = mysql_query($sqlQuery))
{
$total_rows = mysql_num_rows($result);
//se devuelve la información del usuario en una tabla
if($total_rows >= 1)
{
$row = mysql_fetch_assoc($result);?>
<table BORDER="1">
<tr><td>Email: </td>
<td><?php echo $row["Email"]?></td></tr>
<tr><td>Name: </td>
<td><?php echo $row["Name"]?></td></tr>
<tr><td>Surname: </td>
<td><?php echo $row["Surnames"]?></td></tr>
<tr><td>Address: </td>
<td><?php echo $row["Address"]?></td></tr>
<tr><td>City: </td>
<td><?php echo $row["City"]?></td></tr>
<tr><td>CP: </td>
<td><?php echo $row["PC"]?></td></tr>
<tr><td>Phone: </td>
<td><?php echo $row["Phone"]?></td></tr>
</table><?php
}
else
{
echo "Member not found";
}
}
mysql_close($link);
?>
Esta página consulta el socio de la base de datos a partir del identificador pa-
sado por el URL. Una vez obtenido, construye una tabla que será devuelta al
cliente.
1)�Carga�de�la�página�inicial�index.html
<body>
Nº Socio
<input id="memberNumber" width="20" />
<button onclick="searchMember()">Search</button><br><br>
<div id="divRequest"></div>
</body>
2)�Envío�de�la�petición�GET
var id = document.getElementById("memberNumber").value;
var div = document.getElementById("divRequest");
var url = "getMember.php?id=" + id;
oXMLHttpRequest.open('GET', url);
oXMLHttpRequest.onreadystatechange = function(){
...
}
oXMLHttpRequest.send(null);
3)�Generación�de�página�y�respuesta�del�servidor
4)�Recepción�y�actualización�de�página
oXMLHttpRequest.onreadystatechange = function(){
if(oXMLHttpRequest.readyState == 4){
if(oXMLHttpRequest.status == 200){
div.innerHTML = oXMLHttpRequest.responseText;
}
else{
alert("There was a problem with the request.");
}
}
}
• index.html:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Add member form</title>
<script type="text/javascript" src="../script/AJAX.js"></script>
<script type="text/javascript">
function newUser(){
var oXMLHttpObject = createXMLHttpRequestObject();
var form = document.forms[0];
var div = document.getElementById("divRequest");
oXMLHttpObject.open('post', form.action);
oXMLHttpObject.setRequestHeader("Content-Type",
"application/x-www-form-urlencoded");
oXMLHttpObject.onreadystatechange = function(){
if(oXMLHttpObject.readyState == 4){
CC-BY-SA • PID_00172706 43 AJAX
if(oXMLHttpObject.status == 200){
div.innerHTML = oXMLHttpObject.responseText;
}
else{
alert("There was a problem with the request.");
}
}
}
oXMLHttpObject.send(getFormParams(form));
}
function getFormParams(form){
var params = new Array();
for(var i = 0; i < form.elements.length; i++){
var param = encodeURIComponent(form.elements[i].name);
param += "=";
param += encodeURIComponent(form.elements[i].value);
params.push(param);
}
return params.join("&");
}
</script>
</head>
<body>
<form method="post" action="saveData.php" onsubmit="newUser(); return false">
<table>
<tr><td>Email</td><td><input name="email" width="20" /></td></tr>
<tr><td>Nombre</td><td><input name="name"/></td></tr>
<tr><td>Apellidos</td><td><input name="surname"/></td></tr>
<tr><td>Dirección</td><td><input name="address"/></td></tr>
<tr><td>Población</td><td><input name="city"/></td></tr>
<tr><td>CP</td><td><input name="postCode"/></td></tr>
<tr><td>Teléfono</td><td><input name="phone"/></td></tr>
</table>
<input type="submit" value="Save member" /><br>
</form>
<div id="divRequest"></div>
</body>
</html>
• saveData.php:
<?php
CC-BY-SA • PID_00172706 44 AJAX
Una vez presentados los ficheros que forman el ejemplo, explicaremos cada
uno de los pasos involucrados en el alta de un usuario.
1)�Carga�inicial�de�la�página�index.html
</table>
<input type="submit" value="Save member" /><br>
</form>
<div id="divRequest"></div>
Para representar la respuesta del servidor, se declara una división con el iden-
tificador divRequest después del formulario.
2)�Envío�de�la�petición�POST
oXMLHttpObject.open('post', form.action);
Como método, se establece post, y el URL se obtiene del atributo action del
formulario, que en este caso es saveData.php.
oXMLHttpObject.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
function getFormParams(form){
var params = new Array();
for(var i = 0; i < form.elements.length; i++){
var param = encodeURIComponent(form.elements[i].name);
param += "=";
CC-BY-SA • PID_00172706 46 AJAX
param += encodeURIComponent(form.elements[i].value);
params.push(param);
}
return params.join("&");
}
Esta función recorre todos los campos del formulario y añade en una matriz
el nombre del campo seguido de su valor con el formato siguiente:
NombreCampo=ValorCampo.
Finalmente, se utiliza la función join, que devuelve una cadena con todos los
elementos de la matriz concatenados utilizando el carácter pasado por paráme-
tro. De esta forma, el conjunto de parámetros quedaría de la manera siguiente:
NombreCampo1=Valor1&NombreCampo2=Valor2&...
oXMLHttpObject.send(getFormParams(form));
3)�Generación�de�página�y�respuesta�del�servidor
4)�Recepción�y�actualización�de�página
Esto evita que una página web con código malicioso pueda llevar a cabo ata- Internet Explorer utiliza otra
medida, llamada zonas�de�se-
ques o comprometer la confidencialidad de otra página web. guridad, por la que un con-
junto de sitios que cumplen
ciertas normas están exentos
Los navegadores basados en Mozilla consideran que dos páginas tienen un de la política del mismo ori-
gen.
mismo origen cuando el protocolo, el puerto y el dominio son iguales. La tabla
siguiente muestra una serie de URL y si se puede acceder a ellos desde el origen
http://multimedia.uoc.edu/AJAX/index.html:
http://multimedia.uoc.edu/index.html Correcto
http://multimedia.uoc.edu/info/index.html Correcto
La política del mismo origen también afecta a las peticiones realizadas desde
objetos XMLHttpRequest, en los que los URL se limitan al mismo dominio
del que proviene la aplicación. Esta restricción es un gran inconveniente para
muchos desarrolladores que necesitan obtener datos desde dominios cruzados.
Una posible solución es la de utilizar proxies en el lado del servidor, que redi-
rigen las peticiones a servidores en otros dominios y envían de vuelta la res-
puesta a la aplicación.
El W3C publicó una especificación llamada CORS que consiste en el intercam- CORS
bio de cabeceras específicas para informar al navegador de los diferentes do-
CORS es la sigla de Cross-Ori-
minios y métodos HTTP a los que puede acceder. gin Resource Sharing. Firefox
3.5 y Safary 4 la implementan.
Internet Explorer 8 implemen-
1.5. Ventajas e inconvenientes de las diferentes técnicas ta una parte. La especificación
CORS se puede consultar por
Internet en:
Los navegadores guardan en su historial de navegación las diferentes peticio- • Cross-Origin Resource Sha-
ring
nes que se van realizando durante la navegación. De esta manera, el usuario
puede retroceder a URL ya visitados, y también avanzar por éstos. Los URL
CC-BY-SA • PID_00172706 48 AJAX
Otra desventaja son las diferencias entre las implementaciones de esta interfaz Entornos de desarrollo
en los diferentes navegadores. Crear una aplicación web compatible con los
Un entorno de desarrollo (en
navegadores más populares requiere más lógica, un inconveniente que puede inglés, framework) es un con-
ser contrarrestado con el uso de entornos de desarrollo. junto de componentes de soft-
ware utilizados para facilitar el
desarrollo de ciertas funciones.
En este caso, los entornos de
Por otro lado, XMLHttpRequest permite gestionar todos los aspectos de la co- desarrollo de AJAX se pueden
utilizar en un proyecto web
municación con un único objeto: cambio de cabeceras y método de la peti- para añadir funcionalidades
ción, seguimiento de los cambios de estado de la comunicación, código de AJAX.
respuesta del servidor, etc. XMLHttpRequest está pensado para esta labor y
dispone de un amplio conjunto de propiedades, métodos y eventos que no
WAI
ofrecen los marcos ocultos.
WAI es la sigla de Web Accessi-
bility Initiative (‘Iniciativa para
1.6. AJAX accesible la Accesibilidad Web’). Puede
consultarse por Internet en:
• Web Accessibility Initiative
La accesibilidad permite el acceso a la Web y a sus contenidos a personas con (WAI)
• XML Path Language
discapacidad visual, motriz, auditiva, cognitiva o neuronal. La WAI es una ini- (XPath)
ciativa del W3C que se encarga de establecer las especificaciones y las tecno-
logías necesarias para una correcta accesibilidad de la Web para personas con
discapacidad y gente de edad avanzada. Esta iniciativa provee de estrategias,
guías y recursos para la creación de aplicaciones web accesibles.
La mayoría de sitios web no se ajustan a las medidas de esta iniciativa, lo que Tecnologías de apoyo
dificulta el acceso a la Web a la mayoría de gente con discapacidades. A medida
La expresión tecnologías de
que la Web crece rápidamente, se va haciendo cada vez más esencial que ésta apoyo (assistive technologies)
sea accesible, de forma que haya una igualdad en el acceso y las oportunidades se refiere a dispositivos, instru-
mentos, tecnologías o software
para todos. Por ello, la accesibilidad no sólo depende de las tecnologías de que se utilizan para incremen-
tar las capacidades funcionales
apoyo, como programas de lectura de pantalla, sintetizadores de voz, líneas de personas con discapacidad.
braille, etc. La mayor responsabilidad reside en los desarrolladores web y en el
software utilizado para producir y evaluar la accesibilidad.
CC-BY-SA • PID_00172706 49 AJAX
Enlaces de interés
Uno de los objetivos de la WAI es el desarrollo de guías y técnicas que
describan soluciones de accesibilidad para aplicaciones web. Estas guías Este enlace muestra los pa-
sos básicos para llevar a cabo
son consideradas un estándar internacional para la accesibilidad web. proyectos web con accesibili-
dad:
Implementation Plan for
Web Accessibility
Para valorar la accesibilidad y detectar problemas relacionados con ella, existen
Este otro enlace muestra in-
herramientas que ayudan en la evaluación de una aplicación web. Aun así, la formación detallada para
desarrolladores:
evaluación humana es imprescindible.
Web Content Accessibility
Guidelines (WCAG) Over-
view
WAI-ARIA define la manera de hacer más accesibles las aplicaciones web en-
riquecidas que disponen de contenidos dinámicos y una interfaz de usuario
avanzada con controles desarrollados con AJAX y tecnologías relacionadas. WAI-ARIA
Estos controles deben interactuar con las tecnologías de apoyo que utilizan las
WAI-ARIA es la sigla de Web
personas con discapacidades. Por ejemplo, la actualización dinámica de con- Accessibility Initiative - Access
tenido en una página puede no ser detectada por un lector de pantalla que rich Internet applications (‘Ini-
ciativa para la Accesibilidad
utilice una persona invidente. Web - Acceso a aplicaciones
de Internet enriquecidas’). Se
encarga de la iniciativa para la
accesibilidad de aplicaciones
1.6.1. Ejemplo de AJAX accesible RIA.
(9)
WAI-ARIA define el término regiones activas9 AJAX, que permiten que las tec- En inglés, live regions.
1)�La�propiedad�aria-live
Esta propiedad indica la prioridad con la que una tecnología de apoyo debe
tratar las actualizaciones en regiones activas. Los valores posibles son los si-
guientes:
• off. Es el valor por defecto e indica que la región no es activa. Valor rude
• assertive. Este valor indica una prioridad alta, pero no es necesario in-
terrumpir la actividad actual del usuario.
• rude. Éste es el valor más alto, e indica que la actividad del usuario ha de
ser interrumpida cuando se actualice el contenido.
2)�La�propiedad�aria-atomic
1 NODE_ELEMENT
2 NODE_ATTRIBUTE
3 NODE_TEXT
4 NODE_CDATA_SECTION
8 NODE_COMMENT
9 NODE_DOCUMENT
10 NODE_DOCUMENT_TYPE
11 NODE_DOCUMENT_FRAGMENT
El primer nodo de la jerarquía del documento es de tipo Document. Todo ob- Objeto de documento
jeto de documento contiene este tipo de nodo, y es el que se proporciona al
Un objeto de documento es
crear un DOM. A partir de este nodo, se tiene acceso a todo el documento. una instancia del DOM del do-
cumento.
Los diferentes tipos de nodos que aparecen son Document, Element, Attri-
bute, Text y Comment. Los nodos de tipo Node_Element corresponen a to-
das las etiquetas HTML, <html>, <head>, <title>, <body>, <form>, <p>,
<select> y <option>. La elipse que depende del nodo <p> representa un
nodo de tipo Attribute. Los textos del título del documento, los del atributo
“Align” y los de las opciones <option> son nodos de tipo Text.
(10)
MSXML10 es un conjunto de bibliotecas COM de Microsoft para el procesa- MSXML es la sigla de Microsoft
XML Core Services.
miento de XML. MSXML ofrece una implementación del DOM a partir de la
que se pueden crear y manipular documentos.
Para crear un objeto DOM y cargar un documento XML desde Internet Explo-
rer, se debe instanciar MSXML como un objeto ActiveX.
function createDOMXML(){
var aVersions = ["MSXML2.DOMDocument.6.0",
"MSXML2.DOMDocument.5.0",
"MSXML2.DOMDocument.4.0",
"MSXML2.DOMDocument.3.0",
"MSXML2.DOMDocument",
"Microsoft.XmlDom"];
for(var i = 0; i < aVersions.length; i++){
try{
var oXmlDom = new ActiveXObject(aVersions[i]);
return oXmlDom;
} catch(oError){
}
}
throw new Error("Couldn't create a MSXML instance.");
}
oXML = createDOMXML();
Una vez obtenido el objeto, se puede emplear para conseguir objetos de docu-
mentos XML a partir de dos métodos, load(sURL) y loadXML(sXML), que
se explican a continuación:
• load(sURL):
CC-BY-SA • PID_00172706 55 AJAX
oXML.load("/XML/xmlFile.xml");
oXML = createDOMXML();
oXML.onreadystatechange = function(){
if(oXML.readyState == 4)
alert("Document loaded.");
};
oXML.load("/XML/xmlFile.xml");
• loadXML(sXMLString):
Firefox ofrece una interfaz para obtener documentos DOM vacíos, es decir,
que no representan ningún documento existente. Se trata del método crea-
teDocument() de la interfaz DOMImplementation.
function getNewDocument(){
if(document.implementation &&
document.implementation.createDocument){
var domXML = document.implementation.createDocument("", "", null);
return domXML;
}
}
En el ejemplo anterior, se asigna una función al evento onload que será in- Carga del documento
vocada cuando el documento esté cargado.
Por defecto, la carga se reali-
za de forma asíncrona. Esto se
A diferencia de MSXML, el objeto obtenido mediante createDocument() puede modificar mediante la
propiedad async del docu-
no dispone de las propiedades readyState y del manejador onreadystate- mento, estableciendo true o
false.
change. Para obtener un documento a partir de una cadena, se puede hacer
uso del método parseFromString de la clase DOMParser. Éste devuelve un
objeto DOM de XML a partir de una cadena de caracteres pasada por paráme-
tro.
var sXML =
"<message>
<from>[email protected]</from>
<to>[email protected]</to>
</message>";
CC-BY-SA • PID_00172706 57 AJAX
Tanto Internet Explorer como Firefox ofrecen soporte para XML. Ambas im-
plementaciones son diferentes e incompatibles, por lo que hacer uso de una
de ellas descartaría a los clientes del resto de navegadores.
function getXMLDOMObject(xmlFileName){
oXMLHttpObject.open('GET', xmlFileName);
oXMLHttpObject.onreadystatechange = function(){
if(oXMLHttpObject.readyState == 4){
if(XMLHttpObject.status == 200){
oXMLDOMObject = oXMLHttpObject.responseXML;
}
else{
alert("There was a problem with the request.");
}
}
XMLHttpObject.send(null);
}
}
La interfaz Node, implementada por los diferentes tipos de nodos, ofrece un Interfaz Node
conjunto de propiedades y métodos para el acceso al documento y su recorrido
La interfaz Node está definida
por él. Las propiedades más importantes se enumeran en la tabla siguiente: en la especificación Document
Object Model Level 1 por el
W3C.
Propiedad Descripción
attributes Proporciona un vector con los atributos del nodo actual. Es sólo aplicable a
nodos de tipo Element.
documentElement Devuelve el nodo que representa el hijo directo del nodo raíz Document.
nextSibling Obtiene el nodo siguiente dentro de la lista de nodos hijos a la que perte-
nece un nodo.
previousSibling Obtiene el nodo anterior dentro de la lista de nodos hijos a la que pertene-
ce un nodo.
Nombre Retorno
getAttribute(sName) Devuelve el valor del atributo con el nombre pasado por pa-
rámetro.
Nombre Retorno
replaceChild(insNode, replNode) Reemplaza un nodo hijo por otro pasado por parámetro.
Los navegadores basados en Mozilla tratan los espacios en blanco y los saltos
de línea de un documento XML como nodos de tipo texto. Al cargar un DOM
XML, se obtiene un documento cuyo árbol contiene más nodos de lo esperado.
Esto afecta al recorrido y a la interpretación del árbol, ya que se recorren nodos
innecesarios.
function removeWhiteSpace(xmlDOM){
var notWhitespace = /\s/;
var i;
for (i = 0; i < xmlDOM.childNodes.length; i++){
var currentNode = xmlDOM.childNodes[i];
if(currentNode.nodeType == 1){
removeWhiteSpace(currentNode);
}
if((notWhitespace.test(currentNode.nodeValue))
&&(currentNode.nodeType == 3)){
xmlDOM.removeChild(xmlDOM.childNodes[i--]);
}
}
return xmlDOM;
}
Esta función recorre todos los nodos del árbol de forma recursiva. Si un nodo es
de tipo texto y contiene un espacio en blanco, se elimina del árbol resultante.
Ved también
Los ejemplos numerados que se presentan a lo largo de todo este subapartado están tam-
bién disponibles en línea.
Ejemplo 10
• lista.xml:
La vista lógica en forma de árbol del documento anterior podría representarse como se
muestra en la figura 9.
Para simplificar la figura, no se muestran los nodos ni los atributos descendientes de los elementos <verduleria> y <carniceria>
• recorrido.xml:
<html>
<head>
<title>Recorrido de un documento XML</title>
<script type="text/javascript" src="../script/AJAX.js"></script>
<script type="text/javascript">
function mostrarLista(){
var oXMLHttpRequest = createXMLHttpRequestObject();
oXMLHttpRequest.open('GET', 'lista.xml',false);
oXMLHttpRequest.send(null);
if((oXMLHttpRequest.readyState == 4)&&(oXMLHttpRequest.status == 200)){
var oDomXML = removeWhiteSpace(oXMLHttpRequest.responseXML);
var oLista = oDomXML.documentElement;
CC-BY-SA • PID_00172706 61 AJAX
function mostrarProductos(oNode){
var aProducts = oNode.childNodes;
var sText = '';
for(var i = 0; i < aProducts.length; i++){
if(aProducts[i].nodeType == 1){
if(aProducts[i].firstChild.nodeType == 3){
sText += 'Producto: ' + aProducts[i].firstChild.nodeValue + '\n';
}
}
}
alert(sText);
}
</script>
</head>
<body>
<button onclick="mostrarLista()">Mostrar lista</button>
</body>
</html>
function mostrarProductos(oNode){
var aProducts = oNode.childNodes;
var sText = '';
for(var i = 0; i < aProducts.length; i++){
if(aProducts[i].nodeType == 1){
CC-BY-SA • PID_00172706 63 AJAX
if(aProducts[i].firstChild.nodeType == 3){
sText += 'Producto: ' + aProducts[i].firstChild.nodeValue + '\n';
}
}
}
alert(sText);
}
La primera línea obtiene una matriz con todos los nodos hijos mediante la
propiedad childNodes. Después recorre todos los nodos de la matriz y, para
cada uno, comprueba que el nodo sea de tipo Element. Si es así, se accederá al
primer nodo hijo, y comprueba que sea de tipo texto, concatenando su conte-
nido con la variable sText, que acumula el texto de los productos. Finalmen-
te, se muestra la cadena construida.
Los nodos de tipo Document ofrecen el método getElementsByTagName(), Nodos de tipo Document y
que devuelve un vector de nodos correspondientes a todas las etiquetas con el Element
nombre pasado por parámetro que dependen del nodo que realiza la llamada. Los nodos de tipo Document
Si este método se aplicara sobre el nodo raíz, el resultado serían todos los ele- y Element disponen del mé-
todo getElementsByTagNa-
mentos <producto> que se encuentren en el documento. me().
Ejemplo 11
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;
charset=ISO-8859-1">
<title>Recorrido de un documento XML</title>
<script type="text/javascript" src="../script/Ajax.js"></script>
<script type="text/javascript">
function mostrarLista(){
var oXMLHttpRequest = createXMLHttpRequestObject();
oXMLHttpRequest.open('GET', 'lista.xml',false);
oXMLHttpRequest.send(null);
//Si se ha obtenido la lista correctamente...
if((oXMLHttpRequest.readyState == 4)&&
(oXMLHttpRequest.status == 200)){
//Se obtiene el objeto de documento
var oDomXML = oXMLHttpRequest.responseXML;
//La variable aProducts obtiene un vector
//con los nodos de tipo producto
var aProductos = oDomXML.getElementsByTagName('producto');
var sText = '';
//Se acumulan los productos en una cadena de caracteres
for(var i = 0; i < aProductos.length; i++){
sText += 'Producto: ' + aProductos[i].firstChild.nodeValue
+ '\n';
}
//Se muestra la lista de productos
alert(sText);
}
else{
alert('XMLHttpRequest: error en la petición.');
}
}
</script>
</head>
<body>
<button onclick="mostrarLista()">Mostrar lista</button>
</body>
</html>
Ejemplo 12
<html>
CC-BY-SA • PID_00172706 65 AJAX
<head>
<meta http-equiv="Content-Type" content="text/html;
charset=ISO-8859-1">
<title>Recorrido de un documento XML</title>
<script type="text/javascript" src="../script/Ajax.js"></script>
<script type="text/javascript">
function mostrarLista(){
var oXMLHttpRequest = createXMLHttpRequestObject();
oXMLHttpRequest.open('GET', 'lista.xml',false);
oXMLHttpRequest.send(null);
//Si se ha recibido la lista correctamente...
if((oXMLHttpRequest.readyState == 4)&&
(oXMLHttpRequest.status == 200)){
//Se obtiene el objeto del documento lista.xml
var oDomXML = oXMLHttpRequest.responseXML;
//Se obtiene un vector con todos los nodos <producto>
var aProductos = oDomXML.getElementsByTagName('producto');
//Se crea una matriz y en cada posición se añade un producto
//con su descripción
var aListaCompra = new Array();
for(var i = 0; i < aProductos.length; i++){
var oNodo = aProductos[i];
var sCantidad = oNodo.getAttribute('cantidad');
var sUnidad = oNodo.getAttribute('unidad');
var sProducto = oNodo.firstChild.nodeValue;
aListaCompra.push(sCantidad + ' ' + sUnidad + ' de ' +
sProducto);
}
//Se concatenan todas las descripciones de productos
//añadiendo un salto de línea entre ellos
alert(aListaCompra.join('\n'));
}
else{
alert('XMLHttpRequest: error en la petición.');
}
}
</script>
</head>
<body>
<button onclick="mostrarLista()">Mostrar lista</button>
</body>
</html>
Una vez obtenido el DOM del documento lista.xml, se obtienen todos los nodos
<producto> mediante el método getElementsByTagName(). Para cada uno, se crea
una cadena que contenga los atributos de cantidad, unidad y texto del elemento, en di-
ferentes variables. La concatenación de las variables se añade en una matriz.
Propiedad Descripción
El siguiente ejemplo carga un documento XML a partir de una cadena. La cadena está
mal formada, lo que provoca un error:
function exception(){
var oDOMXML = createDOMXML();
var sXML =
"<message>[email protected]</from>
<to>[email protected]</to>
</message>";
oDOMXML.loadXML(sXML);
if(oDOMXML.parseError.errorCode != 0){
alert("Couldn't load XML Document: " +
oDOMXML.parseError.reason);
}else{
alert("XML document loaded successfully");
}
}
function exception(){
var sXML = "<message>[email protected]</from><to>[email protected]</to></message>";
var oParser = new DOMParser();
var oDOM = oParser.parseFromString(sXML,"text/xml");
var oXMLSerializer = new XMLSerializer();
var sXMLError = oXMLSerializer.serializeToString(oDOM);
if(oDOM.documentElement.tagName == "parsererror"){
alert("Couldn't load XML Document: " + sXMLError);
}else{
alert("XML document loaded successfully");
}
}
El código anterior comprueba el valor del nodo raíz comparándolo con la ca-
dena "parsererror" para verificar si se ha producido algún error al cargar
el documento. En este caso, el contenido de la variable sXMLError es el si-
guiente:
/DirectorioPadre/DirectorioHijo/doc.txt
C:\DirectorioPadre\DirectorioHijo\doc.txt
<Directory>
<Subdirectory>
<file name="doc.txt"/>
</Subdirectory>
</Directory>
En XPath las expresiones se evalúan sobre un nodo que se conoce como nodo
de contexto y, al ser procesadas, se obtiene un objeto. El tipo de objeto devuelto
a partir de una expresión puede ser uno de los siguientes:
• cadena de caracteres.
En este módulo sólo trataremos las expresiones que devuelven una colección
de nodos de tipo node-set.
Símbolo Descripción
@ Selecciona atributos.
CC-BY-SA • PID_00172706 70 AJAX
Los ejemplos de expresiones que se exponen a continuación parten del siguiente docu-
mento XML:
<menu>
<breakfast time="7:00 am">
<food price="1">toasts</food>
<food price="1">eggs</food>
<food price="3">beans</food>
<drink price="1">Orange juice</drink>
<tea price="1">Tea with milk</tea>
</breakfast>
<lunch time="12:00 pm">
<food price="3">beans</food>
<food price="1">bread</food>
<drink price="2">coke</drink>
</lunch>
<dinner time="8:00 pm">
<food price="3">salad</food>
<drink price="1">water</drink>
</dinner>
</menu>
dinner/food
/menu/dinner/food
Las expresiones absolutas devuelven el mismo resultado sea cual sea el nodo de contexto
dentro de un mismo árbol.
Expresión Descripción
//drink Devuelve todos los nodos de tipo <drink> situados en cualquier par-
te del documento.
drink Devuelve todos los nodos hijos <drink> que sean hijos directos del
nodo de contexto.
drink | food Selecciona todos los elementos <drink> y <food> hijos directos del
nodo de contexto.
function getFirstFoodBreakfast(oDOM){
return oDOM.documentElement.selectSingleNode("/menu/breakfast/food");
}
function getFirstFoodBreakfast(oDOM){
var oBreakfast = oDOM.documentElement.firstChild;
var oneFood = oBreakFast.selectSingleNode("food");
return oneFood;
}
function getAllFoodBreakfast(oDOM){
var allFood = oDOM.documentElement.selectNodes("/menu/breakfast/food");
return allFood;
}
Parámetro Descripción
xpathExpression Es una cadena que contiene la expresión XPath que debe ser evaluada.
CC-BY-SA • PID_00172706 72 AJAX
Parámetro Descripción
contextNode Es el nodo del objeto de documento a partir del que se evaluará la ex-
presión, incluyendo sus nodos hijos.
resultType Es una constante que especifica el tipo de resultado que debe ser de-
vuelto al evaluar la expresión. Estas constantes se encuentran dentro
de la interfaz XPathResult.
result Si se especifica un objeto existente, éste se usará para devolver los re-
sultados. Especificando null, se devolverá un nuevo objeto XPath-
Result.
El resultado de evaluate() es siempre un objeto del tipo indicado por el Enlace de interés
parámetro resultType. Para especificar este parámetro, se puede utilizar una
Se pueden consultar todos
constante definida dentro de la interfaz XPathResult. Por ejemplo, para las los tipos de resultados de una
expresiones XPath que devuelven un tipo simple, se pueden usar las siguientes expresión XPath mediante
XPathEvaluator en este enla-
constantes: ce:
• Document Object Model
XPath.
• XPathResult.NUMBER_TYPE. El tipo devuelto por la expresión es numé-
rico.
• XPathResult.STRING_TYPE. El tipo devuelto por la expresión es una ca-
dena de caracteres.
• XPathResult.BOOLEAN_TYPE. El tipo devuelto por la expresión es boo-
leano.
XPathResult.UNORDERED_NODE_ITERATOR_TYPE
El tipo devuelto es un objeto que permite recorrer los nodos del resultado Excepción
mediante el método iterateNext(). Este método devuelve el siguiente nodo
Si mientras se recorre el re-
del resultado de la expresión. En el caso de que no hayan más, devolverá null. sultado mediante iterate-
Next() se modifica la estruc-
tura del árbol del objeto DOM,
este método generará una ex-
cepción.
CC-BY-SA • PID_00172706 73 AJAX
La función getFoodArray devuelve una matriz con todos los nodos de tipo <food>
dentro del árbol.
function getFoodArray(oDOM){
var oEvaluator = new XPathEvaluator();
var sXPath = "//food";
var iterator = oEvaluator.evaluate( sXPath,
oDOM.documentElement,
null,
XPathResult.UNORDERED_NODE_ITERATOR_TYPE,
null);
var aFood = new Array;
try{
var thisNode;
while(thisNode = iterator.iterateNext())
{
aFood.push(thisNode);
}
return aFood;
}
catch(e){
alert('Error: The tree was modified during iteration' + e);
}
}
Mediante el método iterateNext, se recorren cada uno de los nodos obtenidos para
añadirlos en la matriz. Todo esto está incluido dentro de un bloque try para capturar una
excepción en el caso de que el árbol del documento sea modificado en el momento en
que se accede a los resultados.
Hasta este punto hemos estudiado cómo obtener un documento XML, cómo
recorrer su estructura mediante el DOM y cómo seleccionar diferentes partes
con XPath. El ejemplo 13 pone en práctica cada uno de estos aspectos.
Ejemplo 13
Ved también
Consta de una página en la que se muestran los diferentes productos del documento
lista.xml. Un combo permite seleccionar una o todas las categorías de productos que Ved el documento
se deben visualizar. Para cada producto se muestra su imagen y un texto a partir de sus lista.xml en el subapartado
2.3 de este módulo didáctico.
atributos mediante HTML dinámico.
CC-BY-SA • PID_00172706 74 AJAX
• listaCompra.html:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Ejemplo XML DOM</title>
<script type="text/javascript" src="../script/Ajax.js"></script>
<script type="text/javascript">
//Se obtiene una instancia de XMLHttpRequest
oXMLHttpRequest = createXMLHttpRequestObject();
//variable que representará el objeto del documento lista.xml
oListaCompra = null;
}
</script>
</head>
<body onload="getListaCompra()">
<select id="seccionesCompra" disabled="disabled"
onclick="cambiarCategoria()">
<option>Toda la lista</option>
<option>Verdulería</option>
<option>Carnicería</option>
<option>Lácteos</option>
</select>
<div id="divListaCompra"></div>
</body>
</html>
a)�createXMLHttpRequestObject()
b)�getListaCompra()
}
}
}
oXMLHttpRequest.send(null);
}
c)�mostrarLista(aProductos)
El bucle for recorre todos los nodos <producto> y, para cada uno, realiza lo
siguiente:
1) Obtiene los valores de los atributos imagen, cantidad y unidad del pro-
ducto en las variables correspondientes. También recupera el texto de la eti-
queta (tag), que describe el nombre del producto mediante la propiedad no-
deValue.
3) Crea una imagen dinámicamente y le asigna los atributos img con el URL y
width y height para establecer el ancho y el alto de la misma. Posteriormente,
añade la imagen dentro de la división.
Después del bucle, la división divLista contendrá aún una división con una
imagen y un texto para cada uno de los productos de la matriz. Las siguientes
líneas después del bucle establecen la división divLista en la división decla-
rada en el documento para su visualización; si ya se había establecido una di-
visión anteriormente, se elimina.
d)�cambiarCategoria()
function cambiarCategoria(){
if(oListaCompra == null) getListaCompra();
var index = document.getElementById('seccionesCompra').selectedIndex;
var sxPathString;
switch(index){
case 0:
sxPathString = '//producto';
break;
case 1:
sxPathString = '/lista/verduleria//producto';
break;
case 2:
sxPathString = '/lista/carniceria//producto';
break;
case 3:
sxPathString = '/lista/lacteos//producto';
break;
}
var aProducts = null;
CC-BY-SA • PID_00172706 78 AJAX
try{
var iterator = oListaCompra.evaluate(sxPathString, oListaCompra, null,
XPathResult.ANY_TYPE, null);
aProducts = new Array();
while(thisNode = iterator.iterateNext()){
aProducts.push(thisNode);
}
}catch(error){
try{
aProducts = oListaCompra.selectNodes(sxPathString);
}catch(error){
alert("Navegador no compatible con XPath");
}
}
mostrarLista(aProducts);
}
(11)
La primera condición comprueba que se haya obtenido el DOM del documen- En inglés, switch.
to lista.xml y, si no es así, lo obtiene llamando a la función getListaCom-
pra(). La tarea siguiente es obtener el índice del combo. En un conmutador11
se evalúa este índice y se asigna a una variable la expresión XPath que permitirá
obtener los nodos <producto> correspondientes a la categoría seleccionada.
(12)
• XSLT12. Este lenguaje define cómo deben formatearse los documentos XSLT es la sigla de extensible
stylesheet language transformations
XML en otros también basados en XML. Puede consultarse la recomenda- (‘lenguaje de hojas de estilo de
ción por Internet en "XLS transformations (XSLT)". transformaciones’).
(13)
XSL-FO es la sigla de extensible
• XSL-FO13. Está orientado a la creación de documentos de diferentes for- stylesheet language formatting ob-
matos no basados en XML. Permite definir otros aspectos de presentación, jects (‘lenguaje de hojas de estilo
para formatear objetos’).
como las características de una página, párrafos, tablas, vínculos, etc.
Formatos no basados en
• XPath. Es el acrónimo de XML Path Language. Es un lenguaje que permite XML
el direccionamiento y la selección de diferentes partes de un documento.
Algunos de los formatos de
salida son documentos PDF,
SVG, RTF, etc.
En esta familia hay que destacar XSLT y XPath, lenguajes que pueden usarse
conjuntamente para transformar documentos XML en documentos XHTML
a partir de una plantilla. El objetivo de este punto es estudiar cómo se pue-
de obtener un documento XML, transformarlo en un documento en formato
XHTML y presentarlo en un área de la interfaz del cliente.
CC-BY-SA • PID_00172706 80 AJAX
<div>
<div>
<img src="./images/patatas.jpg" width="100" height="100">
2 Kg de patatas
</div>
<div>
<img src="./images/zana.jpg" width="100" height="100">
1 Kg de zanahorias
</div>
<div>
<img src="./images/huevos.jpg" width="100" height="100">
1 docena de huevos
</div>
<div>
<img src="./images/jamon.jpg" width="100" height="100">
CC-BY-SA • PID_00172706 81 AJAX
500 gramos de jamón
</div>
<div>
<img src="./images/pollo.jpg" width="100" height="100">
250 gramos de pollo
</div>
<div>
<img src="./images/yogur.jpg" width="100" height="100">
6 unidades de yogur
</div>
<div>
<img src="./images/leche.jpg" width="100" height="100">
2 litros de leche
</div>
</div>
Inicialmente, será necesario obtener los DOM de los documentos XML y XSL. Plantilla de
Para ello se puede utilizar la propiedad responseXML de XMLHttpRequest. transformacion XSL
Una vez obtenidos los DOM, se puede llevar a cabo la transformación a partir
del objeto de documento XML con el método transformNode(oXSL), pa-
sando por parámetro el DOM del documento XSL.
oDOMXML.async = false;
oDOMXSL.async = false;
var sResult = oDOMXML.transformNode(oDOMXSL);
Para llevar a cabo la transformación, se puede hacer uso de uno de los dos
métodos de XSLTProcessor que se enumeran a continuación:
Fragmentos de documento
Los fragmentos de documento son porciones ligeras de objetos de documento. Son útiles
para manipular nodos sin tener que procesar el árbol completo. Una vez procesados estos
nodos, se pueden añadir al documento original.
Ejemplo 14
• listaCompraXSL.html:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Ejemplo de transformación XSL</title>
<script type="text/javascript" src="../script/Ajax.js"></script>
<script type="text/javascript">
oListaXML = null;
oListaXSL = null;
...
}
</script>
</head>
<body onload="cargaDocumentos()">
<button id="button" disabled="disabled" onclick="mostrarXSLT()">
Mostrar transformación
</button>
<div id="divListaCompra"></div>
</body>
</html>
(14)
JSON es la sigla de Java-Script
14
JSON es un formato de datos ligero y abierto que permite el intercam- object notation.
datos que se han de transmitir es de vital importancia. Para más información, po-
déis consultar la página
json.org.
var oPersona = {
"nombre" : "Albert",
"edad" : 25,
"email" : [email protected]
};
alert(oPersona.nombre);
Los objetos y las matrices declarados con la notación literal pueden combinar-
se para crear un objeto con propiedades que sean matrices o una matriz cuyos
valores sean objetos.
var oPersona = {
"nombre" : "Albert",
"edad" : 25,
"email" : "[email protected]",
"carnets" : ["A","B1"]
};
La propiedad "carnets" representa una matriz literal con dos valores. El ac-
ceso al segundo valor de carnets se lleva a cabo tal como sigue:
alert(oPersona.carnets[1]);
De la misma forma, se pueden definir matrices con valores que sean objetos.
var aPersonas = [
{
"nombre" : "Albert",
"edad" : 25,
"email" : "[email protected]"
},
{
"nombre" : "Jordi",
"edad" : 30,
"email" : "[email protected]"
}
];
La matriz anterior define dos objetos con sus respectivas propiedades y valores.
El acceso a una propiedad de un objeto se realizaría indicando el índice y la
propiedad.
alert(aPersonas[1].email);
CC-BY-SA • PID_00172706 90 AJAX
Los sistemas que intercambian datos en JSON deben disponer de un analiza- Analizador sintáctico
dor sintáctico que permita codificar los objetos y las matrices representados
Un analizador sintáctico forma
textualmente en objetos y matrices del propio entorno y viceversa. parte de un compilador o in-
térprete y permite la transfor-
mación de un texto de entrada
Java-Script dispone de la función eval() para analizar y ejecutar código Ja- en objetos y datos del propio
lenguaje.
va-Script a partir de una cadena de caracteres. Si pasamos una cadena de ca-
racteres que contenga un objeto o una matriz en notación literal, eval() de-
volverá una variable que permitirá el acceso a los datos.
• persona.txt:
{
"nombre" : "Albert",
"edad" : 25,
"email" : "[email protected]"
}
• eval.html:
...
var oXMLHttpRequest = createXMLHttpRequestObject();
oXMLHttpRequest.open('GET','persona.txt',false);
oXMLHttpRequest.send(null);
var sCadena = oXMLHttpRequest.responseText;
var oPersona = eval('(' + sCadena + ')');
alert(oPersona.nombre);
...
<application>
<users>
<user>
<name>Joan Perelló</name>
<id>34532</id>
<email>[email protected]</email>
</user>
<user>
<name>Silvia Pons</name>
<id>12598</id>
<email>[email protected]</email>
</user>
</users>
<administrators>
<admin>
CC-BY-SA • PID_00172706 92 AJAX
<name>Andrea Álvarez</name>
<id>43229</id>
<email>[email protected]</email>
</admin>
<admin>
<name>Enrique Fernández</name>
<id>41321</id>
<email>[email protected]</email>
</admin>
</administrators>
</application>
Por otro lado, el fichero que contiene los datos en formato JSON es el siguiente:
{
"application" :
{
"users" : [
{
"name" : "Joan Perelló",
"id" : 34532,
"email" : "[email protected]"
},
{
"name" : "Silvia Pons",
"id" : 12598,
"email" : "[email protected]"
}
],
"administrators" : [
{
"name" : "Andrea Álvarez",
"id" : 43229,
"email" : "[email protected]"
},
{
"name" : "Enrique Fernández",
"id" : 41321,
"email" : "[email protected]"
}
]
CC-BY-SA • PID_00172706 93 AJAX
}
}
Este formato, aunque pueda parecer más extenso por el número de líneas,
contiene menos caracteres, 317, y éstos representan la misma información.
Esto significa que los mismos datos en formato JSON ocupan 113 caracteres
menos.
Pese a su reducido tamaño, JSON puede resultar más inteligible para los hu-
manos que el formato XML. Esto puede no ser un problema si los datos inter-
cambiados no requieren la interpretación humana.