XMLHttpRequest Object
Objetivo del capítulo:
Entender y utilizar conversaciones AJAX con javascript.
El objeto XMLHttpRequest
de javascript apareció a finales del siglo pasado cuando Microsoft lo incorporó como una API en su navegador Explorer 5 como parte de la tecnología DHTML (Dynamic HTML).
No fué hasta el 2006 que la W3C lo incorporara como un estandard y los navegadores modernos se apropiaran de él para hacer conversaciones asincrónicas con el servidor (AJAX).
var xhr = new XMLHttpRequest();
xhr.open('GET', 'ruta/al/archivo.json', true);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
alert(xhr.responseText);
}
};
xhr.send();
Como vemos en el ejemplo, primero creamos un objeto instanciando la API, también necesitamos definir, al menos, la URL o ruta al archivo en el servidor con el que queremos conversar y el método que vamos a transmitir los datos, que en este ejemplo es GET
. Si evaluamos los estados del nuevo objeto creado en la variable xhr con onreadystatechange
, podemos evaluar la respuesta y si el estado está completo ("4") y no hay error en el status ("200"), entonces podemos utilizar el método responseText
con los datos que respondió el servidor en nuestra página. Vamos por partes.
XMLHttpRequest
Lo primero es crear un objeto que va a utilizar la API de javascript "XMLHttpRequest" y sus métodos.
var xhr = new XMLHttpRequest();
Ahora que tenemos un objeto con los métodos del API en la variable xhr, podemos comenzar a interactuar con los métodos y pasar los valores necesarios para la conversación con el servidor.
open
El método open()
permite definir los parámetros de la conversación que queremos generar. Ésta generalmente lleva dos valores: El método de la conversación (GET
ó POST
) y la URL del destino en el servidor. Aunque se pueden definir 3 más: si va a ser una conversación asincrónica (qué es el método predeterminado) o sincrónica, y si necesita usuario y clave para conectarse con el archivo en el servidor.
xhttp.open("GET", "ruta/productos.json",false,"usuario","clave");
En este ejemplo, aparte de traer un archivo .json
, le dice que la conversación es sincrónica y manda usuario y clave para validarse en el servidor.
send
El método open()
no envía los datos, define el tipo de conversación, por lo que siempre está conectado con el método send()
, que es el encargado final de ejecutar la conversación.
xhr.send();
send data
Cómo buena parte de nuestras conversaciones AJAX pueden enviar datos que recogemos del usuario, para procesar en el servidor.
var data = JSON.stringify({
nombre: 'John',
apellido: 'Doe',
email: 'jd@nn.cl'
});
xhr.send(data);
En este ejemplo preparamos la variable data con un objeto con datos, convertido en un JSON con la método de javascript stringify( )
.
onreadystatechange
Como AJAX se ocupa para enviar y recibir datos del servidor, se suele definir sobre el mismo nuevo objeto creado de conversación, que hacer con los datos recibidos. El método onreadystatechange
es el que se encarga de evaluar en qué estado está la conversación.
Aquí es muy útil entender 2 métodos que vienen con el objeto: readyState
, que es el estado actual de la conversación y el status
, que se refiere al tipo de respuesta entregado por el servidor y sobre el que se puede colgar nuestra programación .
if var xhr = new XMLHttpRequest();
xhr.open('GET', 'ruta/al/archivo.json', true);
xhr.onreadystatechange = function () {
alert("El estado ha cambiado.");
};
xhr.send();
En este ejemplo, levantamos una ventana con el método alert()
cada vez que el estado cambie.
readyState
Desde que creamos el nuevo objeto XMLHttpRequest, se está monitoreando su estado. Se maenjan 5 estados que se pueden evaluar:
estado | descripción |
---|---|
0 | No se ha realizado una inicialización del objeto |
1 | Conection establecida con el servidor |
2 | Envío recibido |
3 | Procesando el requerimiento |
4 | Consulta terminada y la respuesta está lista |
En función de estos estados se puede gatillar diferentes feedback y por supuesto, hacer el "debug" de nuestra porgramación cuando no funcione.
if var xhr = new XMLHttpRequest();
xhr.open('GET', 'ruta/al/archivo.json', true);
xhr.onreadystatechange = function () {
(xhr.readyState === 0) {
console.log('Estado 0: UNSENT - La solicitud no ha sido inicializada.');
} else if (xhr.readyState === 1) {
console.log('Estado 1: OPENED - Se ha llamado a open().');
} else if (xhr.readyState === 2) {
console.log('Estado 2: HEADERS_RECEIVED - Se ha recibido la cabecera de la respuesta.');
} else if (xhr.readyState === 3) {
console.log('Estado 3: LOADING - Se está descargando el cuerpo de la respuesta.');
} else if (xhr.readyState === 4) {
console.log('Estado 4: DONE - La operación ha terminado.');
if (xhr.status === 200) {
console.log('Respuesta exitosa:', JSON.parse(xhr.responseText));
} else {
console.error('Error: ' + xhr.status);
}
}
};
xhr.send();
En este ejemplo, escribimos en la consola cada vez que cambie el estado del objeto xhr. Esto bien podría ser un javascript que levanta mensajes o íconos del proceso de conversación para darle feedback al usuario. Una vez que el proceso está terminado y la respuesta es 200 o sea ok, muestra la respuesta responseText
del servidor y además, cómo pedimos un JSON, lo parsea para poder utilizarlo como objeto en la alimentación de nuestro contenido.
status
Por otro lado tenemos el método status
de la respuesta del servidor, ésta está amarrado al los códigos de respuesta HTTP.
rango | descripción |
---|---|
(100 –199 |
Respuestas informativas |
200 –299 |
Respuestas satisfactorias |
(300 –399 ) |
Redirecciones |
(400 –499 ) |
Errores de los clientes |
(500 –599 ) |
errores de los servidores |
var xhr = new XMLHttpRequest();
xhr.open('POST', 'ruta/al/archivo.php', true);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) { // La solicitud ha terminado
if (xhr.status === 200) {
console.log('Estado 200: OK - La solicitud fue exitosa.');
console.log('Respuesta:', JSON.parse(xhr.responseText));
} else if (xhr.status === 201) {
console.log('Estado 201: Created - El recurso fue creado con éxito.');
} else if (xhr.status === 400) {
console.log('Estado 400: Bad Request - La solicitud es inválida.');
} else if (xhr.status === 401) {
console.log('Estado 401: Unauthorized - La autenticación es necesaria.');
} else if (xhr.status === 404) {
console.log('Estado 404: Not Found - El recurso no fue encontrado.');
} else if (xhr.status === 500) {
console.log('Estado 500: Internal Server Error - Error en el servidor.');
} else {
console.log('Estado ' + xhr.status + ': Ocurrió un error inesperado.');
}
}
};
var data = JSON.stringify({
nombre: 'Caperucita',
apellido: 'Roja',
email: 'caperuza@deferoz.cl'
});
xhr.send(data);
xhr.send();
En este ejemplo, enviamos los datos a un script de php y escribimos en la consola los casos clásicos de respuesta que nos puede dar el servidor o un gran génerico al final del bucle.
responseText
El contenido de responseText en XMLHttpRequest depende del tipo de datos que el servidor envía en la respuesta. El servidor puede enviar datos en diferentes formatos, y la cabecera de la respuesta "Content-Type" juega un papel crucial en la interpretación de esos datos.
tipo Texto
El texto plano otext/plain
es el formato más simple. responseText
contiene una cadena de texto sin formato.
Ideal para respuestas cortas, como mensajes de estado o respuestas donde no se requiere un formato estructurado.
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Texto Plano</title>
</head>
<body>
<h1>Texto Plano</h1>
<button id="loadPlainText">Cargar Texto Plano</button>
<pre id="destino"></pre>
<script>
document.getElementById('loadPlainText').addEventListener('click', function() {
var xhr = new XMLHttpRequest();
xhr.open('GET', 'ruta/al/texto:plano.txt', true);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
document.getElementById('destino').textContent = xhr.responseText;
}
};
xhr.send();
});
</script>
</body>
</html>
En este caso, agregamos como contenido de texto en el objeto con id "destino" el contenido recibido en responseText
.
tipo HTML
El tipo HTML (text/html
) es donde responseText
contiene un fragmento de HTML. Utilizado cuando el servidor envía como resultado HTML que puede ser inyectado directamente en el DOM de la página.
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>HTML</title>
</head>
<body>
<h1>HTML</h1>
<button id="loadHTML">Cargar HTML</button>
<div id="destino"></div>
<script>
document.getElementById('loadHTML').addEventListener('click', function() {
var xhr = new XMLHttpRequest();
xhr.open('GET', 'ruta/al/archivo.html', true);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
document.getElementById('destino').innerHTML = xhr.responseText;
}
};
xhr.send();
});
</script>
</body>
</html>
En este ejemplo, se carga como contenido HTML interpretado con el método innerHTML
en el objeto con el id "destino" el fragmento recibido en el método responseText
tipo XML
Los tipo XML (application/xml
o text/xml
) de responseText
contiene datos en formato XML, aunque a menudo se usa más responseXML
en lugar de responseText
para parsear y trabajar con un documento XML.
Utilizado en aplicaciones que dependen de datos estructurados, como configuraciones o respuestas de servicios web SOAP.
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>XMLHttpRequest con XML</title>
</head>
<body>
<h1>Selecciona un Usuario</h1>
<form>
<label for="users">Usuarios:</label>
<select id="users">
<option value="">Cargando usuarios...</option>
</select>
</form>
<script>
document.addEventListener('DOMContentLoaded', function() {
var xhr = new XMLHttpRequest();
xhr.open('GET', 'ruta/al/archivo.xml', true);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
var xmlDoc = xhr.responseXML;
var users = xmlDoc.getElementsByTagName('user');
var select = document.getElementById('users');
select.innerHTML = '';
for (var i = 0; i < users.length; i++) {
var id = users[i].getElementsByTagName('id')[0].textContent;
var name = users[i].getElementsByTagName('name')[0].textContent;
var option = document.createElement('option');
option.value = id;
option.textContent = name;
select.appendChild(option);
}
if (users.length === 0) {
var noDataOption = document.createElement('option');
noDataOption.value = '';
noDataOption.textContent = 'No hay usuarios disponibles';
select.appendChild(noDataOption);
}
} else if (xhr.readyState === 4 && xhr.status !== 200) {
var select = document.getElementById('users');
select.innerHTML = '<option value="">Error al cargar los usuarios</option>';
}
};
xhr.send();
});
</script>
</body>
</html>
En este ejemplo, la respuesta XML se parsea con el objeto responseXML
y después se recorre la estructura a partir de su TagName agregandolo como <option>
del <select>
con el id "users"
tipo JSON
Las respuestas tipo JSON (application/json
)de responseText
contiene una cadena de texto JSON que representa un objeto o un arreglo. Muy común en aplicaciones modernas, ya que JSON es ligero, fácil de interpretar en JavaScript y ampliamente soportado.
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Alumnos - XMLHttpRequest con JSON</title>
<style>
table {
width: 100%;
border-collapse: collapse;
}
table, th, td {
border: 1px solid black;
}
th, td {
padding: 8px;
text-align: left;
}
th {
background-color: #f2f2f2;
}
</style>
</head>
<body>
<h1>Lista de Alumnos</h1>
<button id="loadData">Cargar Datos de Alumnos</button>
<table id="studentsTable">
<thead>
<tr>
<th>ID</th>
<th>Nombre</th>
<th>Correo Electrónico</th>
</tr>
</thead>
<tbody>
<tr>
<td colspan="3">Cargando datos...</td>
</tr>
</tbody>
</table>
<script>
document.getElementById('loadData').addEventListener('click', function() {
var xhr = new XMLHttpRequest();
xhr.open('GET', 'ruta/alumnos.jsom', true);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
var students = JSON.parse(xhr.responseText);
var tableBody = document.getElementById('studentsTable').getElementsByTagName('tbody')[0];
tableBody.innerHTML = '';
students.forEach(function(student) {
var row = document.createElement('tr');
var idCell = document.createElement('td');
idCell.textContent = student.id;
row.appendChild(idCell);
var nameCell = document.createElement('td');
nameCell.textContent = student.name;
row.appendChild(nameCell);
var emailCell = document.createElement('td');
emailCell.textContent = student.email;
row.appendChild(emailCell);
tableBody.appendChild(row);
});
if (students.length === 0) {
var noDataRow = document.createElement('tr');
var noDataCell = document.createElement('td');
noDataCell.textContent = 'No hay alumnos disponibles';
noDataCell.colSpan = 3;
noDataRow.appendChild(noDataCell);
tableBody.appendChild(noDataRow);
}
} else if (xhr.readyState === 4 && xhr.status !== 200) {
var tableBody = document.getElementById('studentsTable').getElementsByTagName('tbody')[0];
tableBody.innerHTML = '<tr><td colspan="3">Error al cargar los datos</td></tr>';
}
};
xhr.send();
});
</script>
</body>
</html>
En este ejemplo, se "parsea" o reconvierte en objeto con el método de javacript JSON.parse
y una vez objeto, agrega filas y celdas con datos a la tabla para mostrarlo.
tipo CSV
La respuesta en responseText
puede contener datos en formatos menos comunes como CSV (text/csv
) . Muy utilizado para datos tabulares o tabulados.
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Alumnos - XMLHttpRequest con CSV</title>
</head>
<body>
<h1>Lista de Alumnos</h1>
<button id="loadData">Cargar Datos de Alumnos</button>
<ol id="alumnos">
<li>Cargando datos...</li>
</ol>
<script>
document.getElementById('loadData').addEventListener('click', function() {
var xhr = new XMLHttpRequest();
xhr.open('GET', 'ruta/estudiantes.csv', true);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
var csv = xhr.responseText;
var lines = csv.split('\n');
var alumnos = document.getElementById('alumnos');
alumnos.innerHTML = '';
lines.forEach(function(line) {
if (line.trim() !== '') {
var cells = line.split(',');
var listItem = document.createElement('li');
listItem.textContent = `ID: ${cells[0]}, Nombre: ${cells[1]}, Correo: ${cells[2]}`;
alumnos.appendChild(listItem);
}
});
if (lines.length === 0 || (lines.length === 1 && lines[0].trim() === '')) {
var noDataItem = document.createElement('li');
noDataItem.textContent = 'No hay alumnos disponibles';
alumnos.appendChild(noDataItem);
}
} else if (xhr.readyState === 4 && xhr.status !== 200) {
var alumnos = document.getElementById('alumnos');
alumnos.innerHTML = '<li>Error al cargar los datos</li>';
}
};
xhr.send();
});
</script>
</body>
</html>
En este ejemplo, se toma el valor de responseText
y se separa en objetos, primero por salto de línea y después por su caracter separador ",". Con el objeto "parseado" se construye lineas <li>
para agregar en la lista ordenada con el id <alumnos>.
Nota:
Como se ve en los ejemplos de los tipos de datos, la programación que vayamos a utilizar para manipular los datos va a depender del tipo de dato que se recibe y qué queremos hacer con él en la página.
Crear una Aplicación de Datos de Usuario
- Crea una página HTML con un botón para cargar los datos de los usuarios y una sección para mostrar los resultados.
- Utiliza
XMLHttpRequest
para hacer una solicitud a un archivo y muestra los datos recibidos en la página. Maneja diferentes estados de la solicitud y muestra mensajes de error si la solicitud falla.