Issues when working with AJAX

Introduction

AJAX, or asynchronous Javascript and XML, is one of those buzz-words causing much excitement among the web-savvy. As a method of quietly communicating with a server it allows web sites to act more like traditional applications, and is an integral part of the web 2.0 methodology. It plays an important role in Flickr, Google Maps, and Backpack, among others.

This paper covers the main stumbling-blocks you’re likely to come across with AJAX.

AJAX and its pros and cons have been discussed elsewhere (Adaptive Path’s article and the Wikipedia entry are good starting-points) so there’s no need to do so again; instead this paper covers the main stumbling-blocks you’re likely to come across with AJAX: instantiating the necessary Javascript object, building the request, and using the response.

This paper is far more technical than is usual for a Mercurytide white paper, and is intended for a audience comfortable with web programming. Any comments are welcome.

Sending a request

To send an AJAX request you first need an instance of an XMLHttpRequest object. If the world was a programmer-friendly place it would take one line of code; but it’s not and instead takes eighteen:

function getNewXMLHttpRequest() {
   var obj;
     try {
       // For Internet Explorer.
       obj = new ActiveXObject('Microsoft.XMLHTTP');
     }
     catch(e) {
       try {
         // Gecko-based browsers, Safari, and Opera.
         obj = new XMLHttpRequest();
     }
     catch (e) {
       // Browser supports Javascript but not XMLHttpRequest.
       obj = false;
       }
     }
     return obj;
}


The code first attempts to create an object using Internet Explorer’s method (notice Explorer doesn’t support XMLHttpRequest natively but relies on an ActiveX control) and if that fails will then try to create an object using the method favoured by Gecko-based browsers, Safari, and Opera. It returns false if the browser supports Javascript but not XMLHttpRequest — covering Explorer on the Mac and versions older than 5.0 on Windows, older versions of Opera and Safari, and very old versions of Gecko-based browsers.

To instantiate an object and send a request takes a minimum of three lines for a GET request (shown below as five for readability):

var request = getNewXMLHttpRequest();
request.open('GET',
    'http://example.org/?var1=newton&var2=darwin&var3=brunel',
    false);
request.send(false);


Or if you want to use the POST method:

var request = getNewXMLHttpRequest();
request.setRequestHeader('Content-Type',
    'application/x-www-form-urlencoded');
var params = 'var1=newton&var2=darwin&var3=brunel';
request.open('POST', 'http://example.org/', false);
request.send(params);


Notice if you want to use the POST method you need to set the content-type header with setRequestHeader().

You can use setRequestHeader() to add almost any header to the request. One idiosyncrasy is the referrer header: depending on the browser this may or may not be sent, and may or may not be writable. The table below sumarises this behaviour.

Referrer sentReferrer writableCan use custom headers
 
Internet Explorer Yes No Yes
Firefox No Yes Yes
Safari Yes No Yes
Opera Yes No Yes

If you want to be sure of a server receiving the referrer, it’s recommended you use a custom header:

request.setRequestHeader('X-Referer', document.location);


It’s quite likely you’ll need to send more than just simple characters in the request, but if you range outside of alphanumeric characters you’ll have to encode them for use in a URI:

var params = 'moneySymbol=' + encodeURIComponent('€');
request.send(params);


One important point: in most cases, browser’s limit XMLHttpRequest communication to the server the web page came from (and not where the script is hosted). If your page is hosted on http://example.org/ you can only make requests to that server — you couldn’t, for example, make a request to http://maps.google.com/. There are ways around this, the most promising being an application proxy — a program written in your favourite programming language that sits on your web server responding to XMLHttpRequests from users, making web service calls, and sending the data back to the client-side script. This and other techniques are discussed by Jason Levitt in Fixing AJAX: XMLHttpRequest considered harmful.

Using the server response

Once you receive a response from a server you can access its data with two properties: responseXML and responseText. As their names suggest they hold an XML document-object representation and a text version respectively. Both are useful depending what you want to do with the data; each is discussed below.

The responseXML property

If the requested script returns well-formed XML you can access this property using DOM functions just as you can the web page itself. If your script returns the following:

<reply>
    <vcard>
       <firstname>Thomas</firstname>
        <surname>Rabaix</firstname>
    </vcard>
</reply>

 


You can get the first name with:

var firstName =
    request.responseXML.getElementsByTagName('firstname')[0];


Notice a frustrating limitation in the code above: to get a single element it gets all firstname elements using the getElementsByTagName() method and returns the first element in the array. So why not give the element an id and use getElementById()?

The answer is there is no way of knowing what attribute — or attributes — acts as an id. The document doesn’t have a DTD or include a namespace, the two methods a browser can use to determine which attributes are ids. HTML and XHTML sensibly use id, but only because this is defined by the W3C. The XML document above doesn’t link to any definition. The W3C has a recommendation for including a default id attribute in XML (see xml:id Version 1.0), but until browsers implement this you’ll have to work round the limitation.

In some cases you may want to receive an HTML document fragment to include in the web page, in which case you’ll need a string serialisation of the document fragment to insert. As often with Javascript, this varies by browser: Gecko-based browsers, Safari, and Opera use the XMLSerializer object while Internet Explorer has an xml property. The following function will return a serialisation regardless of browser; the next section shows how to add such a string serialisation to a document.

function getXMLNodeSerialisation(xmlNode) {
     var text = false;
     try {
         // Gecko-based browsers, Safari, Opera.
         var serializer = new XMLSerializer();
         text = serializer.serializeToString(xmlNode);
     }
     catch (e) {
         try {
             // Internet Explorer.
             text = xmlNode.xml;
         }
         catch (e) {}
     }
     return text;
}


Update: up to at least version 2.0, XMLSerializer only works in Safari if you want to get a serialisation of the root node.

The responseXML property is useful if you want to receive error or status messages as well as document fragments. But in many cases it’s an unnecessary complication, and it’s much easier to use the responseText property.

The responseText property

Similar to the return value of the getXMLNodeSerialisation() function above, the responseText property stores the entire response as a string. If all you want to do with the response is insert it into the web page this is the easier of the two properties to use. Again there is a different method used by Internet Explorer from other major browsers; the code for all is below:

var parentElement = document.getElementById('parent');
try {
     parentElement.innerHTML = request.responseText;
}
catch (e) {
     // IE fails unless we wrap the string in another element.
     var wrappingDiv = document.createElement('div');
     wrappingDiv.innerHTML = request.responseText;
     parentElement.appendChild(wrappingDiv);
}


Notice a peculiarity with Internet Explorer where responseText cannot be added directly to the document. Instead, it has to be added to a created element which is then added to the document. This is only true when responseText is involved; if we were trying to set parentElement.innerHTML to any other string it wouldn’t fail.

The code above, while being the easiest way to insert a response into a document, is not the ‘correct’ method of doing things. Indeed, it would fail in any document sent with the MIME-type application/xhtml+xml. While the standards-compliant DOM-method would be technically correct, it is more verbose and the method above works well.

Further work

Thus far the white paper has covered the basic techniques needed to build an AJAX application or library, but there’s plenty more that could be done. For instance, what if Javascript is returned by the response? Most browsers will ignore it (Internet Explorer will not add script elements to a document, newer Gecko-based browsers will execute it in some circumstances), but there are ways to evaluate it.

And what of embedding AJAX inside an AJAX server response? For anything more than simple scripts you will need to register the embedded XMLHttpRequest object as a global so it can be used elsewhere in the page.

And of course, a problem suffered by many AJAX-based projects: how to keep structure separate from behaviour. This latter problem will be discussed in a future white paper; the two former problems — along with any others — are left to your ingenuity.

Also left unmentioned is the DOM level three load and save specification that is the standards-compliant method of making AJAX-like server-calls. This currently enjoys enough browser-support that it could be widely-used, and may become the method of choice. For an introduction, see Peter-Paul Koch’s Import XML Document.

Summary

This paper has covered the most commonly-encountered problems when first starting AJAX-based programming. It has shown how to write cross-browser code to instantiate an XMLHttpRequest object, how to build an AJAX request, and how to use the server response. It has also mentioned further work necessary to encompass more advanced techniques.

References

Some rights reserved logo

This white paper is an adaptation of the article Issues when developing AJAX libraries by the author, and is licenced under the Creative Commons Attribution-ShareAlike 2.0 licence.

Mercurytide is a forward thinking, dynamic, and innovative Internet applications development company. We are well-established with a proven track-record within the industry of attracting blue chip clients from around the world. We produce regular white papers on a variety of technology-orientated topics. For more details see the Mercurytide web site.


www.mercurytide.co.uk would like to store information (cookies) on your computer. By continuing to use this site, you consent to this.
More info