AJAX made easy

This document describes the 3 steps for making AJAX requests to a web server from the JavaScript running within a web browser.

For more details, please refer to the Wikipedia articles on XMLHttpRequest and AJAX.


1. Creating the XMLHttpRequest object

The first thing you need is an object that can be used to send asynchronous requests to the web server. It's important to note that requests can only be sent to the server from which the JavaScript was itself loaded – otherwise I might write some JavaScript code that might delete all your web mail messages, or forward them on to me, without you knowing.

Here's the code to create this object. MS Internet Explorer does it differently to all other browsers, and there are lots of variations on this code that you might find on the Internet, but you basically just copy and paste it at the top of your JavaScript program.


    // Get object required to connect to server - try browser-specific type first.
    globalServerObject = null
    try
    {
        globalServerObject = new ActiveXObject( "Msxml2.XMLHTTP" )
    }
    catch ( dummyObjectException )
    {
        try
        {
            globalServerObject = new ActiveXObject( "Microsoft.XMLHTTP" )
        }
        catch ( dummyObjectException2 )
        {
            globalServerObject = null
        }
    }
    
    // Default to generic request object if not found.
    if ( globalServerObject == null && typeof XMLHttpRequest != "undefined" )
    {
        globalServerObject = new XMLHttpRequest()
    }

2. Sending a request to the server

Now that that's out of the way, you can actually initiate a request to the server.


    // Start sending the request to the server.
    globalServerObject.open( "GET", "/update.php?" + vRequestData, true )
    globalServerObject.onreadystatechange = receiveServerResponse
    globalServerObject.send( null )

3. Receiving the server response

The final part is the function required to handle the response, even if it's just a status code telling you if a record was deleted successfully, etc.


    // Handles the response from the server after data has been loaded from it.
    
    function receiveServerResponse()
    {
        // Ensure that the response has been fully received from the server.
        if ( globalServerObject.readyState != 4 )
        {
            return
        }
    
        // Handle any server error that occurred.
        // Note: The 200 OK status is given even if the server didn't reply!
        //       Also, Opera gives 403 even when the server sends 401 Unauthorised!
        try
        {
            if ( globalServerObject.status != 200 )
            {
                alert( 'Failed with status ' + globalServerObject.status + ': ' +
                       globalServerObject.statusText, 1 )
                return
            }
        }
        catch ( dummyException )
        {
            alert( "Sorry, the server isn't working properly at the moment." )
            return
        }
    
        // Display a single value from the top level of the XML.
        var vResponseData = globalServerObject.responseXML
        var vRootNode = vResponseData.getElementsByTagName( 'data' ).item( 0 )
        alert( 'Key = ' + findElement( vRootNode, 'key' ) )
    
        // Find the multi-value list data within the XML.
        var vUsersNode = vRootNode.getElementsByTagName( 'list' ).item( 0 )
        vItemList = vUsersNode.getElementsByTagName( 'entries' )
    
        // For list entry returned by the server...
        var vDataNode, vTitle, vDescription
        for ( var vCount = vItemList.length - 1; vCount >= 0; vCount-- )
        {
            // Get the name and value from the current entry.
            vDataNode = vItemList[ vCount ]
            vTitle = findElement( vDataNode, 'title' )
            vDescription = findElement( vDataNode, 'description' )
    
            // Display the name/value.
            alert( 'Title = ' + vTitle + ', value = ' + vDescription )
        }
    }

Supporting function

Stepping through XML data can be a complicated process, but if you've only got a single value to extract from a named node in the XML tree then it's easy to search for it by name and get the value using a function like the one below. Having a function like this reduces each search to a simple function call, as shown by the example uses of it above.


    // Searches from the given XML root node to find the element with the specified
    // name, returning its value as a string.
    
    function findElement( rootNode, itemName )
    {
        // Return an empty string if the node is missing (it may just be empty).
        var xmlNode = rootNode.getElementsByTagName( itemName ).item( 0 ).firstChild
        if ( xmlNode == null )
        {
            return ""
        }
    
        // Otherwise, return the value from the node as a string.
        return "" + xmlNode.nodeValue
    }

Final note

The only thing missing from this example is a way to work out what request was sent, in order to process the response correctly when it eventually arrives. This might be done by indicating it in a value in the XML returned, or by using a different function to handle each one, with multiple server objects. But I prefer to queue the requests in the JavaScript and only process one at a time – using a global variable to track the type of the request. This simplifies the server processing and the XML, but also means that the requests are handled one a time in the order that they are generated, preventing concurrency problems (e.g. where the server deletes a message before attempting to mark it as read).


© Lee J Haywood, 2007-2011