Hace algún tiempo hice un tutorial de Ajax con jQuery, JSON y PHP. Hoy vengo con lo mismo pero sin jQuery, es decir, vamos a ver como trabajar con Ajax y JSON utilizando JavaScript puro, sin ningún framework ni biblioteca. Volvemos a los orígenes de tecnología Ajax: crear un objeto XMLHttpRequest con el que JavaScript puede comunicarse con el servidor de forma asíncrona intercambiando datos entre el cliente y el servidor sin interferir en el comportamiento actual de la página. Comúnmente, la respuesta desde servidor es utilizada para realizar manipulaciones posteriores en el DOM; por ejemplo, cambiar el contenido mostrado en la página.

Construyendo el objeto XMLHttpRequest y enviando la solicitud

El objeto XMLHttpRequest es el responsable de comunicarse con el servidor de forma asíncrona. Crear este objeto necesita, como mínimo, la URL que se va a solicitar al servidor, la cuál puede ser cualquier tipo de recurso, y no sólo XML (aunque su nombre sea XMLHttpRequest). Por ejemplo, la URL solicitada podría devolver HTML, JavaSript, CSS, texto plano, imágenes o, lo que nos ocupa en este tutorial, JSON.

Ejemplo con método GET

En el método GET, los parámetros de la solicitud se añaden a la URL como query string, es decir, se añade a la URL una cadena del tipo param1=value1&param2=value2:

// Definimos la URL que vamos a solicitar via Ajax
var ajax_url = "http://mysite.com/json/data.json";

// Definimos los parámetros que vamos a enviar
var params = "parametro=valor&otro_parametro=otro_valor";

// Añadimos los parámetros a la URL
ajax_url += '?' + params;

// Creamos un nuevo objeto encargado de la comunicación
var ajax_request = new XMLHttpRequest();

// Definimos como queremos realizar la comunicación
ajax_request.open( "GET", ajax_url, true );

//Enviamos la solicitud
ajax_request.send();

Parémonos un momento en el método XMLHttpRequest.open. Este método admite varios parámetros, que son: (os remito a la documentación para más informatión3)

  • método: el método HTTP a utilizar en la solicitud Ajax. GET o POST, en el ejemplo anterior fue GET.
  • url: dirección URL que se va a solicitar, o lo que es lo mismo, la URL a la que se va enviar la solicitud.
  • async: true (asíncrono) o false (síncrono). Es opcional y el valor por defecto es true. Hay quien recomienda ponerlo siempre por si no se respeta el valor por defecto.

Ejemplo con método POST

Si se utiliza el método POST, la solicitud necesita ser enviada como si fuera un formulario (Content-type: application/x-www-form-urlencoded o multipart-form-data). Los datos se envían, al igual que antes en el ejemplo con el método GET, como query string pero en este caso se envían como parámetro del método XMLHttpRequest.send y no como parte de la URL:

// Definimos la URL que vamos a solicitar via Ajax
var ajax_url = "http://mysite.com/json/data.json";

// Definimos los parámetros que vamos a enviar
var params = "parametro=valor&otro_parametro=otro_valor";

// Creamos un nuevo objeto encargado de la comunicación
var ajax_request = new XMLHttpRequest();

// Definimos como queremos realizar la comunicación
ajax_request.open( "POST", ajax_url, true );
// Ponemos las cabeceras de la solicitud como si fuera un formulario, necesario si se utiliza POST
ajax_request.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
//Enviamos la solicitud junto con los parámetros
ajax_request.send( params );

Enviar una solicitud POST con JSON

Había dicho que el formato de los datos enviados en la solicitud POST tiene que ser tipo query string (key=valor&key2=valor2) y que se tienen que envíar como un formulario (Content-type: application/x-www-form-urlencoded). Bien, no es cierto.

En realidad podemos hacer un POST con los datos en cualquier formato, por ejemplo en JSON:

// El JSON a enviar
var myjson = '{ "key" : "value", "key1" : "value1", "key2" : "value2" }'
var ajax_request = new XMLHttpRequest();
ajax_request.open( "POST", ajax_url, true );
// Establecer la cabecera Content-Type apropiada
ajax_request.setRequestHeader("Content-Type", "application/json; charset=UTF-8");
// Enviar la solicitud
ajax_request.send( myjson );

Pero PHP seguirá esperando que el cuerpo de la solicitud sea un query string. Al recibir un cuerpo con otro formato, no podremos acceder desde PHP a esos datos en la superglobal $_POST como es habitual. Tendremos que acceder directamente al cuerpo de la solicitud y decodificar el JSON:

/// Obtenemos el json enviado
$data = file_get_contents('php://input');
// Los convertimos en un array
$data = json_decode( $data, true );

Por eso decía que los datos se debían enviar como query string, porque en PHP no tiene mucho «sentido» enviarlos en otros formatos. Sin embargo, puede ser apropiado si en el servidor se utiliza otro lenguaje. Por ejemplo, enviar JSON mediante POST puede ser apropiado para servidores que utilicen Node.js.

Recepción de la respuesta

Ya sabemos cómo enviar la solicitud al servidor. Para recibir la respuesta se utiliza la propiedad XMLHttpRequest.onreadystatechange para ejecutar una función cuándo XMLHttpRequest.readyState cambia de valor. El valor de readyState que más nos interesa es 4; este valor indica que la solicitud Ajax ha concluido y la respuesta desde el servidor está disponible en XMLHttpRequest.responseText:

// Definimos la URL que vamos a solicitar via Ajax
var ajax_url = "http://mysite.com/json/data.json";

// Definirmos los parámetros que vamos a enviar
var params = "parametro=valor&otro_parametro=otro_valor";

// Añadimos los parámetros a la URL
ajax_url += '?' + params;

// Creamos un nuevo objeto encargado de la comunicación
var ajax_request = new XMLHttpRequest();

// Definimos una función a ejecutar cuándo la solicitud Ajax tiene alguna información
ajax_request.onreadystatechange = function() {

    // readyState es 4
    if (ajax_request.readyState == 4 ) {

        // Analizaos el responseText que contendrá el JSON enviado desde el servidor
        var jsonObj = JSON.parse( ajax_request.responseText );
        // La variable jsonObj ahora contiene un objeto con los datos recibidos

    }
}

// Definimos como queremos realizar la comunicación
ajax_request.open( "GET", ajax_url, true );

//Enviamos la solicitud
ajax_request.send();

Trabajando con el formato JSON en JavaScript

Aunque el propio nombre Ajax signifique Asynchronous JavaScript And XML y el objeto utilizado para la comunicación cliente-servidor se llame XMLHttpRequest, con Ajax se puede solicitar en realidad cualquier tipo de datos: XML, HTML, texto plano, JavaScript, CSS y cualquier otro formato que se te pueda ocurrir, incluido el formato JSON que hoy en día es el formato de elección para intercambio de datos cuándo se trabaja con Ajax.

JSON son las siglas de JavaScript Object Notation (Notación de Objeto JavaScript). Los objetos JavaScript tienen una estructura compuesta por pares key : value (clave : valor):

{key : value, key2 : value2, key3 : value3, ...}

En el siguiente ejemplo la variable misDatos es un objeto JavaScript y tiene tres elementos cuyas key son nombre, apellidos y edad, y se puede acceder a sus valores con misDatos.name, misDatos.apellidos y misDatos.edad.

// La variable misDatos contiene un objeto JavaScript
var misDatos = {"nombre" : "Antonio", "apellidos" : "Molina Ballesteros", "edad" : 35};

console.log( misDatos.nombre );
console.log( misDatos.apellidos );
console.log( misDatos.edad );

Los objetos pueden ser multidimensionales; por ejemplo:

{
key : value,
key2 : {
        subkey  : subvalue,
        subkey2 : subvalue2
    }
}

Y se accede a los subkeys como jsonObj.key2.subkey, jsonObj.key2.subkey2, etc:

// La variable misDatos contiene un objeto JavaScript
var misDatos = {
    "nombre" : "Antonio",
    "apellidos" : "Molina Ballesteros",
    "edad" : 35,
    "direccion" : {
        "calle" : "Gran Via, 2",
        "ciudad" : "Madrid"
    }
};

console.log( misDatos.nombre );
console.log( misDatos.apellidos );
console.log( misDatos.edad );
console.log( misDatos.direccion.calle );
console.log( misDatos.direccion.ciudad );

JSON.parse

La respuesta JSON en una solicitud Ajax es un string, no es un objeto JavaScript sino una cadena de texto con notación de objeto JavaScript, esto es JSON. Es necesario convertir esta cadena a un objeto antes de intentar trabajar con los datos. Esta conversión se realiza con el método JSON.parse. El siguiente ejemplo es exactamente igual que el anterior pero la variable misDatos es un string (fíjate en las comillas) y es necesario aplicar JSON.parse para convertirlo en un objeto JavaScript:

// La variable misDatos es un string en notación JSON, pero no un objeto
var misDatos = '{ "nombre" : "Antonio", "apellidos" : "Molina Ballesteros", "edad" : 35, "direccion" : { "calle" : "Gran Via, 2", "ciudad" : "Madrid"} }';

// Se pasa el string a un objeto JSON
var jsonObj = JSON.parse( misDatos );

// Ahora, la variable jsonObj contiene el valor de cada key accesible en la forma jsonObj.key
console.log( jsonObj.nombre );
console.log( jsonObj.apellidos );
console.log( jsonObj.edad );
console.log( jsonObj.direccion.calle );
console.log( jsonObj.direccion.ciudad );

Ejemplo Ajax e interacción con PHP

Ya hemos visto como construir una solicitud Ajax y como trabajar con objetos en JavaScript de forma básica, así que estamos listos para combinar ambos. Vamos a ver un ejemplo en el que se envía una solicitud Ajax a un script PHP que devuelve información en formato JSON. Voy a utilizar el mismo ejemplo que en el tutorial mencionado al principio de este tutorial, tan sólo cambia el JavaScript utilizado, el PHP es exactamente igual y no lo voy a repetir aquí, aunque puedes descargarte el ejemplo completo más abajo.

<!DOCTYPE html>
<html lang="es-ES">
<head>
  <meta charset="UTF-8">
 
  <script>
 
    //getdeails será nuestra función para enviar la solicitud ajax
    var getdetails = function( id ){
      // Definimos la URL que vamos a solicitar via Ajax
      var ajax_url = "personas.php";

      // Definimos los parámetros que vamos a enviar
      // Debería trabajar en hacer esto un poco más limpio, de momento vale para hacer funcionar el ejemplo
      var params = '';
 
      id = JSON.parse(id).toString();

      id = id.split(",");
 
      if( id.length > 1 ) {
        //Si hay más de un id, enviar como query string array
        for( elem in id ) {
          params += "id[]=" + id[elem] +"&";
        }
      } else {
        params = "id=" + id;
      }
 
      //Añadimos los parámetros a la URL
      ajax_url += '?' + params;

      // Creamos un nuevo objeto encargado de la comunicación
      var ajax_request = new XMLHttpRequest();

      // Definimos una función a ejecutar cuándo la solicitud Ajax tiene alguna información
      ajax_request.onreadystatechange = function() {

        // see readyState es 4, proseguir
        if (ajax_request.readyState == 4 ) {

          // Analizaos el responseText que contendrá el JSON enviado desde el servidor
          var response = JSON.parse( ajax_request.responseText );
 
          if( response.success ) {
 
            var output = "<h1>" + response.data.message + "</h1>";
            //recorremos cada usuario
            for ( user in response.data.users ) {

              output += "<h2>Detalles del usuario " + response.data.users[user].ID + "</h2>";
 
              //recorremos los valores de cada usuario
              for ( userdata in response.data.users[user] ) {
 
                output += '<ul>';
                output += '<li>' + userdata + ': ' + response.data.users[user][userdata] + "</li>";
                output += '</ul>';
 
              }
 
            }
 
            //Actualizamos el HTML del elemento con id="#response-container"
            document.getElementById("response-container").innerHTML = output;

          } else {
 
            //response.success no es true
            document.getElementById("response-container").innerHTML = 'No ha habido suerte: ' + response.data.message;
 
          }

        }
      }
 
    // Definimos como queremos realizar la comunicación
    ajax_request.open( "GET", ajax_url, true );
 
    //Enviamos la solictud con los parámetros que habíamos definido
    ajax_request.send();

  };
 
   // Esperar a onload para poder acceder a los elementos del DOM
   window.onload = function() { 
 
     // Obtener todos los botones que utilizamos para lanzar la solicitud Ajax
     var userbutton = document.getElementsByTagName( "button" );

     for (var i=0; i < userbutton.length; i++) {
 
       // Para boton seleccionado, cuándo se haga click llamar a la funcion gedetails donde manjamos la solicitud ajax
       userbutton[i].onclick = function() {
 
         document.getElementById("response-container").innerHTML = "<p>Buscando...</p>";
 
         // Obtener el valor de data-user, pasar a array si se necesita y ejecutar la funcion getdetails()
         var id = this.getAttribute('data-user');

         getdetails( id );

       }
 
     }

  };
 
 </script>
</head>
<body>
 <p><button class="userdata" data-user="1">Dame los datos de la persona con ID = 1</button> - <button class="userdata" data-user='["1","2","3"]'>Dame los datos de las personas con ID = 1, ID = 2 e ID = 3.</button> - <button class="userdata" data-user="0">Ningún usuario</button></p>
 <div id="response-container"></div>
</body>
</html>

Nota: A continuación puedes descargarte el ejemplo completo, incluido el script PHP. Antes de intentar ver el ejemplo en vivo, revisa el fichero PHP y cambia las credenciales de la base de datos de ejemplo que has tenido que crear previamente.

Descargar el ejemplo completo

Referencias

  1. Objetos globales: JSON. Mozilla Developers Network.
  2. JavaScript Objets. w3schools.
  3. Web API Reference: XMLHttpRequest. Mozilla Developers Network.