An Introduction to Ajax

A few years ago the Web was buzzing with the advent of the so called Web 2.0; the latest incarnation of the web with data driven applications typified by Google Maps. One of the main themes of all these Web 2.0 sites was a newer, slicker interface driven by Ajax. Ajax is a group of interacting technologies that untie a web browser from the refresh button; allowing a web page to display new content without having to reload the entire page. Ajax allows data to be fetched and displayed while the user is interacting with another part of the page, preventing the web browser from freezing while this is happening and allowing web pages to act more like desktop applications. These days there's a growing array of web applications that function very much like their desktop equivalents. Google Apps and others are looking set to challenge Microsoft Offices dominance of the office application market. Without Ajax these sort of web applications would not be possible.

The basic program flow for an Ajax operation is that a client-side script running in a web browser, usually written in JavaScript but not necessarily, sends an asynchronous request to a server, returning program control to the the browser. The server responds with the requested data which is handled by a client-side callback function, again usually written in JavaScript. This callback function then uses the web browsers Document Object Model (DOM) to manipulate the web page to show the desired results. The key to the operation is that after the web browser makes the Ajax request control is returned to the browser allowing the user to continue using the web page. This differs to the usual way a browser makes requests, which causes the web page to freeze while it's waiting for the server to respond. This is illustrated in the diagram below.

Message control for normal and Ajax browser requests

The steps involved in an Ajax request can be summarised as:

  1. Web browser makes an Ajax request.
  2. Scripting engine makes an HTTP request, setting a callback function to handle the response and returns program control to the browser.
  3. Web server receives request from client and performs action specified in the request.
  4. Data requested is found or generated.
  5. Web server sends requested data as HTTP response.
  6. Scripting engine receives response from the web server and passes it to the callback function.
  7. Callback uses the web browsers DOM to manipulate web page

In this article I use the term Ajax as an umbrella term for all the different technologies that allow asynchronous communication between a web browser and a server. I use the term AJAX, or Asynchronous JavaScript and XML, in reference to a specific set of technologies. In particular the ones that use the JavaScript XMLHttpRequest object to facilitate such communication. While this may be a little confusing it is the popular usage.

Making an AJAX Request

The key to an AJAX request is the JavaScript XMLHttpRequest class. This class contains methods capable of sending asynchronous HTTP requests and setting a callback function to handle the HTTP response. It is supported by all the latest browsers and has been standardised by the W3C, older versions of Internet Explorer use an ActiveX object in its place. To use the class a number of steps need to be the followed; first a XMLHttpRequest object must be instantiated (created); a callback function can then be assigned to it's onreadystatechange event handler, to deal with the reply; then the object's open method is called to connect to a server, specifying the HTTP request method (usually GET or POST) and the URI; finally any data associated with the AJAX request is sent, either null, if the method is GET, or the data to send to the server, if the method is POST.

The function below is a general purpose function for sending AJAX requests. It uses the HTTP GET method to send a request to the server encoded in the URI stream. This is the most common way of performing AJAX requests. There are the usual limitations when using the GET method; the size of the URI is limited (about 2,000 characters in Internet Explorer) and the data is public. To send large amounts of data or when encrypting the data with SSL the POST method should be used.

function sendRequest(uri, responseCallback) {
// Try and create an XMLHttpRequest object
try {
// This should work with all modern browsers
xmlhttp = new XMLHttpRequest();
}
catch (e) {
try {
// This should work with IE 5 and IE 6
xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
}
catch (e) {
try {
// This may work in old versions of IE if the last failed
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
catch (e) {
// Unable to create an asynchronous HTTP request
return false;
}
}
}
// Set the callback function, open and send the request
xmlhttp.onreadystatechange = responseCallback;
xmlhttp.open("GET", uri, true);
xmlhttp.send(null);
}

Code snippet 1. JavaScript SendRequest function

The callback function below uses the XMLHttpRequest object, xmlhttp, defined in the sendRequest function above. Before the object is used, it should be checked that it is ready for use by checking its ready state and that the reply from the web server was okay by checking its HTTP status code. The ready state should be 4, Done, and the HTTP status code should be 200, OK. Once these have been checked then it is okay to use the object.

function callback() {
// Check the XMLHttpRequest objects ready state and HTTP status code are
// okay before using the object
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
// Use xmlhttp
}
}

Code snippet 2. JavaScript call back function

Value Status


0 Unsent
1 Opened
2 Headers Receiving
3 Loading
4 Done

Table 1. XMLHttpRequest ready states

Handling the Reply

The fact that the X in AJAX stands for XML would lead people to believe that the reply to the request needs to be XML. This is not the case. While XML is often used the reply could contain any data, though only ones composed of text are usually considered. The most common being HTML, XML and JSON.

If the reply is HTML then the callback function doesn't have to worry about parsing the data to extract the relevant pieces. It can be as simple as finding the relevant page element and using it's innerHTML method to insert the HTML returned by the server. While this can make the programming easier it breaks many ideas about good programming technique; it requires that the server-side script knows exactly what HTML to produce, increasing the dependence between the web page and the server. If the page is changed at a later date then the code on the server needs to be changed as well as the page.

function HTMLcallback() {
// Check the XMLHttpRequest objects ready state and HTTP status code are
// okay before using the object
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
// Replace the HTML inside element with id="id" with AJAX reply
document.getElementById("id").innerHTML = xmlhttp.responseText;
}
}

Code snippet 3. Using a callback function with HTML replies

XML is a good format for the transmission of structured data. In the case of an AJAX request a server-side script can create the reply in XML without any knowledge of how the data will be used by the client, the callback function can then parse the returned XML, manipulating the DOM as required. The disadvantage when compared to HTML replies is the extra coding required to parse the XML on the client.

function XMLcallback() {
// Check the XMLHttpRequest objects ready state and HTTP status code are
// okay before using the object
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
// Check there is XML to parse
if (xmlhttp.responseXML && xmlhttp.responseXML.hasChildNodes()) {
// Get the base XML tag "root"
var xml = xmlhttp.responseXML.getElementsByTagName("root");
// If the browser is IE the XML must be loaded before getting the base tag
if (xml.length == 0) {
xmlhttp.responseXML.load(xmlhttp.responseBody);
xml = xmlhttp.responseXML.getElementsByTagName("root");
}
// Iterate over all the tags who's parent is "root"
for (var i = 0; i < xml.length; i++) {
for (var j = 0; j < xml[i].childNodes.length; j++) {
var data = xml[i].childNodes[j].firstChild.nodeValue;
// Do something with the data
}
}
}
}
}

Code snippet 4. Using a callback function with XML replies

JavaScript Object Notation (JSON) does away with problem of parsing the reply to the AJAX request. The JSON reply is a string that can be evaluated to create a JavaScript object with the data packaged as properties of this object. This does away with the need to do complex parsing as once the string has been evaluated acccessing acessing value is as simple as calling the desired property of the object. I will cover the usage of JSON in detail in another article on JSONP, or JSON with Padding; another Ajax technology that uses dynamic HTML script tags for asynchronous communication.

function JSONcallback() {
// Check the XMLHttpRequest objects ready state and HTTP status code are
// okay before using the object
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
// Evaluate JSON string to create the JavaScript object
var jso = eval('(' + xmlhttp.responseText + ')');
// Do something with jso
}
}

Code snippet 5. Using a callback function with JSON replies

So far only the client-side has been considered. This still leaves the the server-side, specifically how the data is retrieved and processed into a reply (HTML, XML, JSON) for the client. This article is won't cover the server side in detail as the concepts are the same as those for any dynamically generated website. I will illustrate the basic idea with an example of a use for AJAX with MySQL and PHP running on the server.

Example - Filtering one HTML select box with another

This example demonstrates how to use AJAX to filter one select box with another without the need to reload the page. The data for the select boxes is pulled from a MySQL database and returned to the client in XML using the PHP programming language.

Problem: You have a very large table of values, with a foreign key to another table, you would like to display in a drop down select box in an HTML form.

Solution: Use the foreign key in a select box to filter a second select box, hence displaying a smaller menu.

MySQL Tables

The database contains two inter-related tables, the first can be used to generalise the second. In this example the main table, veg, contains a reference to the generalising table, veg_types; the foreign key veg_type_id.

# Generalising vegetable types table
CREATE TABLE examples.veg_types (
id INTEGER UNSIGNED NOT NULL,
name VARCHAR(25) NOT NULL,
PRIMARY KEY(id)
);
# Insert some trial values into the veg_types table
INSERT INTO examples.veg_types (id, type) VALUES
(1, 'Allium'), (2, 'Root'), (3, 'Herbs');
# Main vegetable table
CREATE TABLE examples.veg (
id INTEGER UNSIGNED NOT NULL,
name VARCHAR(25) NOT NULL,
veg_type_id INTEGER UNSIGNED NOT NULL,
PRIMARY KEY(id)
);
# Insert some trial values into the veg table
INSERT INTO examples.veg (id, name, veg_type_id) VALUES
(1, 'Onions', 1), (2, 'Garlic', 1), (3, 'Carrot', 2),
(4, 'Potato', 2), (5, 'Parsnip', 2), (6, 'Parley', 3),
(7, 'Sage', 3), (8, 'Rosemary', 3), (9, 'Thyme', 3);

Code snippet 6. Database tables

HTML Fragment

The web page contains two drop-down select inputs corresponding to each table in the database. The first contains the contents of the generalising table, while to start with the second displays a message to choose an option from the first. In this example the first select box contains the contents of the veg_types table, which will be used to filter the veg table. When the user selects an option from the first drop-down menu an AJAX request is made that will populate the second drop-down menu with the filtered results. This is achieved by attaching the AJAX call the the onchange event handler of the first select box. The function call, sendRequest('filter.php?id='+this.value, vegCallback);, sends the id of the veg_type as an AJAX request to the PHP file filter.php and sets the JavaScript function vegCallback as the callback function to handle the returned XML.

<label for="veg_types">Veg Types</label>
<select name="veg_types" id="veg_types" onChange="sendRequest('filter_veg.php?id='+this.value, vegCallback);">
<option value="1">Allium</option>
<option value="2">Root</option>
<option value="3">Herbs</option>
</select>

<label for="veg">Vegetables</label&gt
<select id="veg" name="veg">

<option>Please select a veg type</option>
</select>

Code snippet 7. HTML select boxes

PHP Code

The PHP program takes the veg_type_id in the GET request, queries the database for the filtered list of veg and returns them in XML format. The script below is a stripped down version of what would be implemented in the real world. It contains very basic type checking and error handling, and nothing to help debugging.

<?php
// Open a connection to the database
if (!$connection = @ mysql_connect('localhost', $username, $password)
exit();
if (!@ mysql_select_db('examples'))
exit();
// Check the id
if (isset($_GET['id']) and is_numeric($_GET['id'])) {
// Query the database
$query = "SELECT * FROM veg WHERE id = {$_GET['id']};";
$results = @ mysql_query($query);
}
else
exit();
// Build XML response
$xml = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
$xml .= "<veggies>\n";
if (isset($results)) {
while ($result = mysql_fetch_array($results)) {
$xml .= " <veg>\n";
$xml .= " <id>{$result['id']}</id>\n";
$xml .= " <name>{$result['name']}</id>\n";
$xml .= " </veg>\n";
}
}
$xml .= "</veggies>\n";
// Send XML to page
ob_get_clean();
header("Content-Type: text/xml; charset=utf-8");
echo $xml;
?>

Code snippet 8. PHP script to return filtered list in XML

JavaScript Callback Function

Finally the JavaScript callback function parses the XML and creates the new HTML option elements in the select box.

function vegCallback() {
// Check the XML reply has been received from the web server
if (xmlhttp.readyState == 4 && xhrobj.status == 200) {
// Get the select box to add the filtered options to
var vegId = document.getElementById('veg');
// Remove any options from the select box
vegId.options.length = 0;
// Check if the data sent from the web server is XML
if (xmlhttp.responseXML && xmlhttp.responseXML.hasChildNodes()) {
// Get the root XML tag
var vegXML = xmlhttp.responseXML.getElementsByTagName("veggies");
// If the browser is IE the XML must be loaded before getting the root tag
if (vegXML.length == 0) {
xmlhttp.responseXML.load(xmlhttp.responseBody);
vegXML = xmlhttp.responseXML.getElementsByTagName("veggies");
}
// Iterate over all the 'veg' tags
for (var i = 0; i < vegXML.length; i++) {
// Get the 'veg' id
var id = vegXML[i].getElementsByTagName("id")[0].childNodes[0].nodeValue;
// Get the 'veg' name
var name = vegXML[i].getElementsByTagName("name")[0].childNodes[0].nodeValue;
// Add a new option with the id and name to the select box
vegId.options[i] = new Option(name, id);
}
}
else {
// If the data isn't XML or has no tags display an error
vegId.options[0] = new Option("Error!");
}
}
}

Code snippet 9. Javascript call back function to display filtered select box

Discussion

This example is rather simplistic, but it does demonstrate the basics of how to construct an AJAX request and handle the reply. The main advantage of using Ajax in this scenario is it removes the requirements of reloading the page to show the filtered list. If this was to be done without AJAX then all the values in the form need to be passed to the server and put back in the form by the server-side script, if the form is simple this will require little extra programming, for complex forms it can help reduce the server-side coding and complexity. There are a number of disadvantages to this; on slow Internet connections or heavily loaded servers the client can be left waiting without any indication of what is going on, and it won't work in web browsers with JavaScript disabled or old web browsers.

Further Reading

Ajax: A New Approach to Web Applications, Jesse James Garrett. Adaptive Path Inc, 18 February 2005.
http://adaptivepath.com/ideas/essays/archives/000385.php

The XMLHttpRequest Object, Anne van Kesteren, editor. W3C, 15 April 2008.
http://www.w3.org/TR/XMLHttpRequest/

6th April 2010