Getting Started with AJAX
See the demo page for the finished version of the code.Using the XMLHttpRequest Object
First, we need a server-side script that we can call. The getCityAndState.asp script will search a database table for a given ZIP code (passed in the query string or as a form field) and return the city and state where that code is assigned.
It returns this information as a single line of plain text in the format city_name,state_abbr. You can try it out with the link below, where the ZIP code is passed in the query string:
getCityAndState.asp?zipCode=89120
For our first example, we set up a form with input fields for ZIP code, city and state that looks something like this:
<form action="/common/formdump.asp" method="post"> <div> ZIP Code: <input name="zipCode" type="text" size="5" maxlength="5" onchange="initiateCityStateLookup(event);" /> City: <input name="city" type="text" size="20" /> State: <input name="state" type="text" size="2" /><br /> <input name="submit" type="submit" value="Submit" /> <input name="reset" type="reset" value="Clear" /> </div> </form>
Note that the ZIP code field has its onchange
event set to
call a function named initiateCityStateLookup()
. The idea is to
dynamically fill in the city and state fields when the user enters a ZIP code.
So let's add the JavaScript code:
var cityStateLookup = getXMLHttpRequest(); function initiateCityStateLookup(event) { // Get the zip code. var zipCode = document.forms[0].elements["zipCode"].value; // Make a request to get the matching city and state. var url = "getCityAndState.asp?zipCode=" + zipCode; cityStateLookup.open("GET", url, false); cityStateLookup.send(null); // Fill in the city and state fields, if available. try { var data = cityStateLookup.responseText.split(","); if (data.length == 2) { document.forms[0].elements["city"].value = data[0]; document.forms[0].elements["state"].value = data[1]; } else alert("ZIP code not found."); } catch (ex) {} }
We first defines a variable named cityStateLookup
as an
XMLHttpRequest object using the function described earlier. When the user
enters a value in the ZIP code field and tabs out, the onchange
event will fire, calling initiateCityStateLookup()
.
That function gets the ZIP code entered by the user from the form field and sets up the URL to make the HTTP request to, passing it as a query string parameter.
The open()
method is called to initialize the request. Note
that the third parameter is set to false, meaning that this
will not be an asynchronous request. In other words, browser will wait for the
request to complete before continuing the code execution.
When a response is received, the data will be returned in the
XMLHttpRequest object's responseText
property. This string can be
parsed to get the ZIP code's matching city and state which are then copied to
the appropriate form fields. If no data is returned, the fields are left
unchanged and an error message is displayed.
You can try it out below:
So far, so good. But there is a potential problem with this code, the fact
that the browser has to wait for the server to respond. There could be a
lengthy delay while the browser waits on the send()
call to
complete. During this time, the browser UI will be unresponsive, making it not
very user-friendly.
Making an Asynchronous Request
To prevent this, we can make an asynchronous request by specifying
true as the third parameter on the open()
call.
This will cause the subsequent call to send()
to return
immediately, so the browser will not halt code execution as it waits for the
server response to complete.
The question then becomes, how do we know when the request completes? For
that, the XMLHttpRequest object provides a read-only property called
readyState
which reflects the current progress of a request. It
also provides an event called onreadystatechange
which, as the
name implies, is raised whenever the readyState
property
changes.
readyState
will have a numeric value between zero and four. The
meaning of each state is shown below:
- 0 - UNINITIALIZED,
open()
has not been called. - 1 - LOADING,
open()
has been called butsend()
has not been called. - 2 - LOADED,
send()
has been called and the response status and headers have been received. - 3 - INTERACTIVE, the response data is being downloaded.
- 4 - COMPLETE, the request has completed and all response data has been downloaded.
For the most part, you can ignore the first four states and only check for
the COMPLETE state. Using our previous example, we can define an
onreadystate
event handler as follows:
function cityStateReadyStateChange() { // Check the ready state. if (cityStateLookup.readyState == XMLHTTPREQUEST_READY_STATE_COMPLETED) { // Fill in the city and state fields, if available. try { var data = cityStateLookup.responseText.split(","); if (data.length == 2) { document.forms[0].elements["city"].value = data[0]; document.forms[0].elements["state"].value = data[1]; } else alert("ZIP Code not found"); } catch (ex) {} } }
Note that we use the constant
XMLHTTPREQUEST_READY_STATE_COMPLETED
defined earlier instead of
hard-coding the number 4
.
We can then alter the code for initiateCityStateLookup()
above
to assign the event handler and make an asynchronous request:
function initiateCityStateLookup(event) { // Get the zip code. var zipCode = document.forms[0].elements["zipCode"].value; // Perform an asynchronous request to get the matching city and state. var url = "getCityAndState.asp?zipCode=" + zipCode; cityStateLookup.onreadystatechange = cityStateReadyStateChange; cityStateLookup.open("GET", url, true); cityStateLookup.send(null); }
This demo does just that, except that it adds some
validation checks on ZIP code field before making the request and will display
a message on the page for each stage of the readyState
as the
request progresses.
One thing we haven't look at are the status
and
statusText
properties, which are available once the ready state
reaches LOADED. The status
property will contain the HTTP status
code returned by the server while statusText
is a short text
description of that code. A successful request will have a status code of 200
meaning "OK." Other common status codes are 404 meaning "Not Found" and
500 meaning "Server Error." You can use these to help determine if valid data
was returned by the server, or for debugging.
function myReadyStateChange() { if (myXMLHttpRequest.readyState == XMLHTTPREQUEST_READY_STATE_COMPLETED) { if (myXMLHttpRequest.status == 200) { // Process the data. ... } else { alert("Error: HTTP " + myXMLHttpRequest.status + " " + myXMLHttpRequest.statusText); } } }
So far, we've seen the Asynchronous and JavaScript parts of AJAX. Next, we'll look at the XML part.