Asynchronous JavaScript and XML (AJAX)

AJAX is a method by which JavaScript executing on a webpage can make requests from the server. These are executed in the background - the browser's location does not change. AJAX is commonly used to load data, synchronize webpages with real-time data (think Facebook feeds and stock tickers), or submit data without leaving the page.

Making AJAX Requests

The basic process for making an AJAX request is to create an XMLHttpRequest object to process the request. The request object is prepared by calling its open() method and providing a HTTP method and url for the request. The request can then be sent with the send() method. If the request has a body, this is passed as the first parameter of send(). This XMLHttpRequest triggers a readystatechange event when the request state changes - typically because the request concluded successfully or with errors. An event handler can be attached to the object to listen for this response, and trigger appropriate action. So the basic template for an AJAX request is:

var xhr = new XMLHttpRequest(); // Set up the request xhr.open('GET', '/projects/'); // Send the request (with optional body) xhr.send(null); // Listen for state changes xhr.onreadystatechange = function() { var DONE = 4; // readyState 4 means the request is done. var OK = 200; // status 200 is a successful return. if (xhr.readyState === DONE) { if (xhr.status === OK) { // If we get here, our request was successful, // for now we'll log the response we received console.log(xhr.responseText); // TODO: Do something with the results } else { // If the status was not OK (200), we had an error console.log('Error: ' + xhr.status); } } }

POSTing Data through AJAX

The above example used a GET request, but what if we need to POST some data? We'd need to rewrite our xhr.open() and xhr.send() to reflect this. Consider the case of posting a JSON string:

var project = { name: "Test Project", description: "This is a test project", version: "v0.1", license: "MIT" }; // Set up the request xhr.open('POST', '/projects/'); // Set the content type xhr.setRequestHeader("Content-Type", "application/json"); // Send the request (JSON body) xhr.send(JSON.stringify(project));

Note that we set a content type to reflect what we're uploading with the xhr.send() method - JSON text. This works well if we build a custom JavaScript object on the client side that we need to upload. Another common use case is wanting to submit form data.

var project = { name: "Test Project", description: "This is a test project", version: "v0.1", license: "MIT" }; // Convert the project to a x-form-urlencoded string var urlencoded = project.keys().forEach(function(key){ return encodeURIComponent(key) + "=" + encodeURIComponent(project[key])); }).join('&'); // Set up the request xhr.open('POST', '/projects/'); // Set the content type xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); // Send the request (x-www-form-urlencoded body) xhr.send(urlencoded);

If our form includes file data, we need to send a multipart form body. The FormData object provides us the ability to build this body. We can either build the form data manually with FormData.append(), or we can pass the form to the FormData constructor:

// Assuming our form has id 'project-form' var form = document.getElementById('project-form'); var data = new FormData(form); // Set up the request xhr.open('POST', '/projects/'); // Set the content type xhr.setRequestHeader("Content-Type", "multipart/form-data"); // Send the request (multipart body) xhr.send(data);

The latter method has the benefit of automatically generating the file attachments.

JQuery AJAX

The JQuery library provides several helpful methods to simplify the creation of AJAX requests.

The core of these is the jQuery.ajax() method, which takes a url and a associative array (JavaScript Object) of options as parameters. The above vanilla JavaScript code could be rewritten as:

jQuery.ajax('/projects/', { method: 'GET', success: function(data) { // If we get here, our request was successful, // for now we'll log the response we received console.log(data); // TODO: Do something with the results }, error: function(xhr, status) { // If the status was not OK (200), we had an error console.log('Error: ' + status); } });

JQuery tries to be intelligent about parsing responses. also provides syntatic sugar for Ajax requests specific HTTP methods in jQuery.get() and jQuery.post().

Another useful function is jQuery.load(), which is expects an http response and will load it into the selected DOM element, i.e.:

jQuery('#content').load('/projects');

Would load the HTML served by the route /projects into the html element with ID of content. This can be quite helpful for single-page applications.

POSTing data with JQuery AJAX is simply a matter of assigning a data property:

var project = { name: "Test Project", description: "This is a test project", version: "v0.1", license: "MIT" }; jQuery.post('/projects', { data: project });

The object passed as the data is automatically serialized as x-www-form-urlencoded. If we want JSON instead, we need to serialize the object, set the content type, and let jQuery know not to process it for us:

var project = { name: "Test Project", description: "This is a test project", version: "v0.1", license: "MIT" }; jQuery.post('/projects', { data: JSON.stringify(project), processData: false, contentType: 'application/json' });

Similarly, we can send a FormData object:

var form = $('#project-form')[0] var project = new FormData(form); jQuery.post('/projects', { data: project, processData: false, contentType: 'multipart/form-data' });

Same-Origin AJAX policy

It is important to note that AJAX request are covered by the Same Origin Policy. For security reasons, browsers will only allow requests against the same origin that the original HTML file was downloaded from. I.e. if any part of the host string (the port, domain, subdomain, or even protocol) is different, the request will be rejected.

It is possible to bypass this restriction through JSONP (JSON with padding), though this approach is being phased out because of security concerns. A more robust approach comes from Cross-Origin Resource Sharing (CORS).