What is AJAX?

The word AJAX is thrown around so often that it is hard to understand what AJAX really is. AJAX itself is not that new of a technology, but recent JavaScript libraries have made it a very popular tool. This has even lead to the widespread use of the term (inappropriately in some cases).

In this post, I will go over the high-level concept of Asynchronous JavaScript and XML for those who just want a better understanding. Then I will delve into the low level technical overview of how AJAX works for those who are interested.

Let’s first break down the acronym AJAX.

Asynchronous: Non-blocking, not synchronous

JavaScript: Also known as ECMA script (and has absolutely NOTHING to do with Java). JavaScript is a high level scripting language often used as a client side scripting tool and is standardized by the ECMA organization (ECMA-262 is the official standard)

XML:

General purpose markup language standardized by the W3C. So at its lowest level, AJAX is a JavaScript methodology for performing asynchronous client-side XML based HTTP requests behind the scenes.

Now, as we will see later, AJAX is based on XML. However, as with most of the latest JavaScript libraries out there, it is most commonly used without XML.

Basam pointed out to me the other day (from a book he was reading) that AJAX doesn’t even require JavaScript. To that author, I say pffffft! Scott Raymond, the author of AJAX on Rails, was referring to a Microsoft scripting language called VB script (those of you who are .NETers, I mean no offense by any of my following statements).

Microsoft’s Internet Explorer (IE) exclusive scripting language has the ability to perform Asynchronous calls since IE uses an ActiveX object to perform the request (unlike the standardized XMLHttpRequest object as we will see later). Instead of calling it AJAX, I would actually call it AVBAX (that is not the official name, just something I came up with because calling a VB script based “AJAX” is a contradiction to the name AJAX itself).

Rants about misnomers aside, we come back to what is at the heart of AJAX. A developer using AJAX (especially in a JavaScript library) should understand that AJAX is a non-blocking JavaScript call that uses standard HTTP protocol and must conform to the protocol.

With JavaScript, an HTTP request can be made using the standard HTTP methods (GET, POST, etc), modify request headers, read response headers, load the XML document returned, or simply read the response as text being returned. Two common practices with current JavaScript libraries is to return HTML or JavaScript instead of XML. XML can become taxing to parse in a client’s browser as it becomes larger and more complex, so returned HTML can be dumped into a DOM object and returned JavaScript can be executed. That’s about the highest level overview that can be given.

Next, I’ll talk about the heart of AJAX and explicit JavaScript details. I’ll try to keep it to where you can understand it even if you don’t know JavaScript, but unfortunately I will have to delve into some of the more gruesome details.

To start off, AJAX is rooted in the XMLHttpRequest object. Now, IE uses an ActiveX object called Microsoft.XMLHTTP (actually there is another one called “MSXML2.XMLHTTP.3.0”, but I don’t know what the difference is. You’ll have to refer to Microsoft for the answer). Now, aside from Microsoft again subverting the standards, they did manage to give their ActiveX object the same functionality as the standardized XMLHttpRequest object. This is the last point that I’ll make to specify differences between IE and everyone else.

The XMLHttpRequest object (hereafter referred to as “XHR”) has three aspects to look at: the request, the response, and all that lies in between. The request follows a distinct process: open the request, set the request headers (optional), and send the request and the related data (if any).

As a security measure, an AJAX request can only request a URL that resides on the same domain as the current page. Now, Microsoft allows Universal Browsing (of which Mozilla Firefox will not even allow), which permits cross domain AJAX calls. This is a severe security loophole in IE (one of the many). Other browsers may allow to Universal Browsing, but I have not extensively researched other browsers. Regardless, Universal Browsing is a security concern because it allows one site to get a handle of a session on another site and make malicious requests. The XMLHttpRequest itself does not protect against Universal Browsing, but browsers should handle this security issue, thereby allowing the developer to often times ignore this issue.

The XHR has an event listener (aka handler) that fires whenever the XHR readyState attribute changes (called onreadystatechange). The XHR readyState goes through 5 stages: unsent, opened, headers received, loading, and done. In the context of this discourse, we will only focus on the done state (you can find the definition of all of the states at http://www.w3.org/TR/XMLHttpRequest/#unsent-state). These states are represented as integers 0 through 4. You assign a function to handle the readyState change, which can be a closure (remember this is JavaScript, so closures are a good concept to use). Now, at any time during the request, you can invoke the XHR abort method, which (for all practical purposes), aborts the request.

So, once you have reached the readyState of 4 (aka DONE), what do you do? Well, whatever you want. However, since XHR is an HTTP request, you must check the status. Fortunately, the XHR allows you to see the status, and even the status text of the HTTP response. This allows you to implement any error handling you want (and yes, JavaScript has try/catch blocks which are my favorite error handling control structure).

Once your XHR is DONE with at status of 200 (OK), then you are free to process the data. This is where freedom comes in. You can access an XML object (JavaScript is designed to crawl through a DOM and XML works beautifully here) and parse it, or you can access the textual representation of the response, which leads to the flexibility of being anything: XML, HTML, JavaScript code (using the eval method in JavaScript; this can be powerful.), JSON (again use the eval method), base 64 encoding of some document, an application specific serialized data structure, etc. This data is available in the XHR’s responseText attribute.

Hopefully this has helped you understand the technology that so many of our higher level frameworks sit on top of. Understanding the low level stuff makes a developer more productive and valuable. Often times we take these things for granted, but understanding how they work allows us to become better developers (especially when things go wrong). You can find a very detailed definition of the XMLHttpRequest object at http://www.w3.org/TR/XMLHttpRequest/. Below is some code that implements AJAX so that you can see it in action.


Code:

// short hand the document.getElementById method
var $ = function(divId) { return document.getElementById(divId); };

//  AJAX will be a javascript object that has methods called update, evaluate, and sendRequest
var AJAX = {
    evaluate: function(url, method, data) {
      AJAX.sendRequest({
        url: url,
        method: method,
        data: data,
        onreadystatechange: function(request) { if(request.readyState == 4 && request.status == 200) eval(request.responseText); }
      });
    },

    sendRequest: function(configs) {
      var self = this;

      // create the request object
      var request = (window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject('MSXML2.XMLHTTP.3.0');

      // upcase the passed in method for continuity
      if(configs.method == null)
        method = 'POST';

      configs.method = configs.method.toUpperCase();

      // create the url encoded query string of the data object passed in
      var dataStr = "";
      for(i in configs.data)
        dataStr += i + "=" + configs.data[i] + "&";

      // if not URL set, use the current one
      if(configs.url == null)
        configs.url = window.location.href;

      // if it is a get request, append the data string to the url
      if(configs.method == 'GET')
        configs.url += "?" + dataStr;

      // open the request
      request.open(configs.method, configs.url);

      // set the headers content type, if not explicitely set by the user
      if(configs.headers != null && configs.headers['Content-type'] == null && configs.headers['Content-Type'] == null && configs.headers['content-type'] == null)
        request.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');

      // set the request header, only required if POST method
      for(header in configs.headers)
        request.setRequestHeader(header, configs.headers[header]);

      // create the onreadystatechange event handler defined by the following closure
      // Note: closures are functions that have the scope of where they are defined
      //       hence, it has access to the request and divId objects
      request.onreadystatechange = function() { configs.onreadystatechange(request) };

      // send the request, if POST, send the request (POST) data
      if(configs.method == 'GET')
        request.send(null);
      else
        request.send(dataStr);
    },

    //  update function will update the DOM object with id divId with the responseText returned
    update: function(divId, url, method, data) {
      AJAX.sendRequest({
        url: url,
        method: method,
        data: data,
        onreadystatechange: function(request) {
        // are we in the done state with an HTTP status of 200 OK
        if(request.readyState == 4 && request.status == 200)
          $(divId).innerHTML += request.responseText;
        }

      });
    }
}