Javascript Tutorial -- Simple AJAX
If you've spent much time on-line in the last few years, you have probably noticed a trend towards very sophisticated and interactive web sites. For example, GMail rivals traditional e-mail clients like Thunderbird or Outlook in its speed and responsiveness, even though it is a web app rather than an application running on your local computer. This is due to a relatively new technology called "AJAX" -- short for "Asynchronous Javascript and XML". -- and it is the new darling of the Internet. While creating something on the scale of GMail is a bit too complicated for an introductory AJAX howto like this, we will learn the basic concepts that lurk under the hood of a typical AJAX application.
A typical AJAX application consists of three parts:
- An AJAX request;
- A server response;
- An update to the web browser as a result of the server response.
Most of this magic is performed through a relatively new method of the "document" object: XMLHttpRequest(). The XMLHttpRequest object handles sending a request to the web server and also handles checking the response codes from the server. Unfortunately, the XMLHttpRequest object is not available in all recent web browsers (this means you, Internet Explorer...sigh). However, Microsoft did create an alternate method that is pretty much compatible with XMLHttpRequest() called "ActiveXObject()", so you can create pages that work with IE as well as Opera, Safari and Firefox. Unfortunately, the ActiveXObject does not work the same way in all versions of IE, so we have to be a little clever when creating our AJAX application. It's a chore to read, but here's how you do it:
//We'll do the "what browser is this?" two-step..
try
{
//Opera 8.0+, Firefox, Safari, etc.:
Request = new XMLHttpRequest();
}
catch (e)
{
try
{
//I.E. -- well-behaved, standards compliant browsers need not apply
Request = new ActiveXObject("Msxml2.XMLHTTP");
}
catch (e)
{
try
{
//I.E. again:
Request = new ActiveXObject("Microsoft.XMLHTTP");
}
catch (e)
{
//It's not working -- eject, eject eject!
return false;
}
}
}
Wow...that was painful -- gotta love Microsoft :rolleyes: Anyway, even though that snippet of code looks really convoluted and ugly, what it's doing is actually pretty simple. First, it tries to use the XMLHttpRequest object. If that fails, it tries to use the first of two Microsoft-only ActiveXObjects. If that fails, the script tries the second way of createing a Microsoft ActiveXObject. If that fails, you're hosed -- bail out of the script gracefully because AJAX does not work on the client browser. Ideally, you would want to leave a message for the user encouraging them to use a browser that works with AJAX like Firefox or Opera.
Now that we have created an object to send and receive data to and from the server, we will introduce a few more methods of this new object. First, we need a way to send a query to the server. This actually is a two step process: first, we create the request, then we send it. These are done with the "open()" and "send" objects, like so:
...
//I am omitting all of the try/catch nonsense needed for IE here
Request = new XMLHttpRequest();
...
Request.open("GET", "./ExampleCGIScript.php", true);
Request.send(null);
The syntax for the "open()" method is as follows:
XMLObjectName.open("GET or PUT", "URL", [async], ["username", ["password"]);
The parameters in the [square brackets] are optional, and may be ommitted.
In addition to sending a request to the server, we also need a way to listen for a response from the server. For this task, we use the "onreadystatechange". Note the capitalization of this method -- although most methods, attributes and objects in Javascript follow the convention of "first word in lower case, subsequent words with the first letter capitalized", as in "thisIsAJavascriptObject", this method is all lower case. THIS IS IMPORTANT -- Javascript is case sensitive, and if you try to use "onReadyStateChange()", your scripts will fail, and you won't know why (trust me -- I've tried it, and took much longer to troubleshoot the problem than I'd like to admit). onreadystatechange() works hand-in-hand with yet another method called "readyState", which will return a response code from 0 to 4, with the following meanings:
| State | Meaning |
| 0 | Uninitialized |
| 1 | Loading |
| 2 | Loaded |
| 3 | Interactive |
| 4 | Complete |
We will be watching for readyState() to change to "4", signifying that the request is complete. When readyState() changes to four, we will run an in-line function to process the returned data. There are three ways that I know of to return the data:
- XML (the "X" in "AJAX";
- plain text;
- JSON (IIRC, "Java Script Object Notation" -- essentially, an extension to Javascript that allows the web server to return a new object, including methods and attributes, that you can use just like any other object in Javascript).
Of these three types of data, JSON is (arguably) the easiest to use, but conceptually the most difficult to understand, so I won't discuss it in this howto -- maybe I'll add a new howto later :) XML is ideal for AJAX, but is a little more difficult to use and understand than plain text, so we'll start with retrieving plain text from the server.
Of course, to retrieve a plain text response from the server, we will need still another Javascript method, this one called "responseText()". The responseText() method returns a string object containing the plain text response from the server, and can use methods such as innerHTML() or write() to write the text to the web browser. For example:
...
document.getElementById('ChangeMe').innerHTML = Request.responseText;
...
Here is an example of a script that uses AJAX to send a request to a web server, and that returns a plain text response and here is the source code for the Javascript page:
<html>
<head>
<title>
Javascript development pages
</title>
<script>
<!--
function AJAXFunction()
{
var Request;
var Name = document.getElementById('name');
var Url = '/cgi-bin/javascript/getAge.pl?name=' + Name.value;
//we'll try the code, then handle exceptions if/when they occur
try
{
//Opera 8.0+, Firefox, Safari, etc.:
Request = new XMLHttpRequest()
}
catch (e)
{
try
{
//I.E.:
Request = new ActiveXObject("Msxml2.XMLHTTP")
}
catch (e)
{
try
{
//I.E. again:
Request = new ActiveXObject("Microsoft.XMLHTTP")
}
catch (e)
{
//Eject, eject eject!
return false
}
}
}
//Try to obtain data from the server:
Request.onreadystatechange = function ()
{
if (Request.readyState == 4)
{
document.getElementById("ChangeMe").innerHTML = Request.responseText
}
}
Request.open("GET", Url, true)
Request.send(null)
}
//-->
</script>
</head>
<body>
<form name="MyForm">
Select a name:
<select name="name" onChange="AJAXFunction()">
<option value="Fred">Fred
<option value="Nancy">Nancy
<option value="George">George
<option value="Adam">Adam
<option value="Mary">Mary
<option value="Melissa">Melissa
</select>
<br>
Age:
<span id="ChangeMe">/span>
</form>
</body>
</html>
Of course, no Javascript/AJAX script would be complete without discussing how to retrieve and parse an XML response from the server. This is done -- you guessed it -- with another Javascript method. Instead of using responseText(), logically enough, an XML file is retrieved with responseXML(). If all you want to do is paste XML into your HTML document, then you can use responseXML() exactly like you used responseText() and use the document.write() method to insert the server response. However, the beauty of XML is that it is both a markup language and a method of storing data elements in a plain text file. To the Javascript developer, this means that it is possible to use the Document Object Model to walk through the XML file just like you would walk through an HTML file -- all of the methods you have learned for traversing the document tree in HTML work for traversing an XML response from a web server. For example, you can use the getElementsByTagName() method to retrieve all of the XML entries with a given tag name. Still with me? Maybe an example will make it a little clearer.
Suppose you have the following XML response to a query:
<?xml version="1.0" ?>
<!DOCTYPE weightandbalance SYSTEM "http://www.gecko-ak.org/javascript/weightandbalance.dtd">
<weightandbalance>
<airplane>
<nnumber>N12345</nnumber>
<emptyweight>1075</emptyweight>
<grossweight>1700</grossweight>
<emptycg>39.25</emptycg>
</airplane>
<airplane>
<nnumber>N54321</nnumber>
<emptyweight>1492</emptyweight>
<grossweight>2550</grossweight>
<emptycg>38.24</emptycg>
</airplane>
<airplane>
<nnumber>N600LW</nnumber>
<emptyweight>535</emptyweight>
<grossweight>1050</grossweight>
<emptycg>116.99</emptycg>
</airplane>
</weightandbalance>
This XML file describes weight and balance information for three airplanes, and consists of data elements for the N-Number, the empty weight, the gross weight and the empty center of gravity for these three airplanes. By using the responseXML() method in conjunction with getElementsByTagName(), you can pull just the information that you want from XML file:
...
var BaseNode = document.getElementById('weightandbalanceinfo');
var Data;
var EmptyCG;
var EmptyWeight;
var GrossWeight;
var NewNode;
var NNumber;
var Request;
var XML;
var i;
...
Request.onreadystatechange = function ();
{
if (Request.readyState == 4)
{
XML = Request.responseXML.documentElement;
//first, loop over every occurrence of "nnumber" in the XML
for(i = 0; i < XML.getElementsByTagName('nnumber');
{
NNumber = XML.getElementsByTagName('nnumber')[i].firstChild.nodeValue;
EmptyWeight = XML.getElementsByTagName('emptyweight')[i].firstChild.nodeValue;
GrossWeight = XML.getElementsByTagName('grossweight')[i].firstChild.nodeValue;
EmptyCG = XML.getElementsByTagName('emptycg')[i].firstChild.nodeValue;
NewNode = document.createElement('p');
BaseNode.appendChild(NewNode);
NewNode = document.createTextNode('N-Number: ' + NNumber);
BaseNode.appendChild(NewNode);
NewNode = document.createTextNode(' Empty Weight: ' + EmptyWeight);
BaseNode.appendChild(NewNode);
NewNode = document.createTextNode(' Gross Weight: ' + GrossWeight);
BaseNode.appendChild(NewNode);
NewNode = document.createTextNode(' Empty Center of Gravity: ' + EmptyCG);
BaseNode.appendChild(NewNode);
NewNode = document.createElement('hr');
BaseNode.appendChild(NewNode);
}
}
}
Request.open("GET", "/javascript/Example25-2.SimpleAJAX.xml", true);
Request.send(null);
...
Here is the above script in action.