Getting Started with AJAX
See the HttpRequest demo page for the finished version of the code.Using the HttpRequest Object
For this demo, we'll combine the functionality of the two previous examples. The user will be able to look up a ZIP Code based on the city and state they have entered and vice-versa.
In the code, we'll use one HttpRequest object to handle either request. We first define it and set up a "Content-Type" request header so we can POST the relevant data.
var postRequest = new HttpRequest(); postRequest.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); postRequest.failureCallback = requestFailed;
We also assign a failure callback function to display any error to the user should a request fail.
function requestFailed(httpRequest) { setStatusText("Lookup failed, HTTP " + httpRequest.status + " " + httpRequest.statusText + "."); }
The user initiates the look up by clicking one of two links on the page. The
first will look up a list of ZIP Codes based on the city and state entered by
the user. It does this via a call to getZipCodes()
. The code is
essentially the same as before, except we now use the HttpRequest object we've
defined rather than an XMLHttpRequest object.
function getZipCodes(event) { // Clear the status text. setStatusText(""); // Check for a valid city and state. var city = document.forms[0].elements["city"].value; var state = document.forms[0].elements["state"].value; if (city.length == 0 || state.length != 2) { setStatusText("Enter a city and two-letter state abbrev."); return; } // Reset the zip code drop down list. resetZipCodeList(); // Encode the data to be POSTed. var city = encodeURI(city); var state = encodeURI(state); // Perform an asynchronous request to get a list of zip codes for that // city and state. postRequest.url = "getZipCodes.asp"; postRequest.successCallback = zipCodeCallback; postRequest.post("city=" + city + "&state=" + state); }
Note that we only need to set the URL and callback function. The object's
post()
method will handle opening the connection, setting the
request headers and sending the data.
When the request completes successfully, the zipCodeCallback()
function will be called to process the data returned. Again, this is similar
to the previous callback handling except that we don't need to check the
ready state or status codes. We already know we got a valid response and we
just need to check the data returned.
function zipCodeCallback(httpRequest) { // Assume no matches were found. statusText = "No ZIP Codes found." // Set focus back on the city field. document.forms[0].elements["city"].focus(); // Get the XML document returned from the request and fill in the form // fields. try { var xmlDoc = httpRequest.responseXML; // Copy the city and state attributes from the root XML node to the // appropriate form fields. var city = xmlDoc.documentElement.getAttribute("city"); var state = xmlDoc.documentElement.getAttribute("state"); if (city.length > 0 && state.length > 0) { document.forms[0].elements["city"].value = city; document.forms[0].elements["state"].value = state; } // Get all the zip code tags returned from the request. var els = xmlDoc.getElementsByTagName("zipCode"); // Set the status text. statusText = els.length + " ZIP Codes found." // Add a dummy option to the zip code drop-down list. var option = document.createElement("OPTION"); option.text = "Select one..."; option.value = ""; try { document.forms[0].elements["zipCodeList"].add(option, null); } catch(ex) { // For IE. document.forms[0].elements["zipCodeList"].add(option); } // Save the current zip code. var zipCode = document.forms[0].elements["zipCode"].value; var found = false; // Add an option to to the drop-down list for each zip code returned // from the request. for (var i = 0; i < els.length; i++) { // Create the option. option = document.createElement("OPTION"); option.text = option.value = els[i].firstChild.nodeValue; // Is the current zip code in the list? if (option.text == zipCode) found = true; // Add the option to the list. try { document.forms[0].elements["zipCodeList"].add(option, null); } catch(ex) { // For IE. document.forms[0].elements["zipCodeList"].add(option); } } // If the current zip code is not in the list, clear it. if (!found) document.forms[0].elements["zipCode"].value = ""; // Show the drop down list and set focus on it. document.forms[0].elements["zipCodeList"].style.visibility = ""; document.forms[0].elements["zipCodeList"].focus(); } catch (ex) {} // Display the status message. setStatusText(statusText); }
The other link works similarily, making a call to the
getCityState()
function. We just need to set up a different URL
and callback function.
function getCityState(event) { // Clear the status text. setStatusText(""); // Check for a valid zip code. var zipCode = document.forms[0].elements["zipCode"].value; if (zipCode == "") { setStatusText("Enter a ZIP Code."); return; } if (!zipCode.match(zipCodeFormat)) { setStatusText("Invalid ZIP Code."); return; } // Reset the zip code drop down list. resetZipCodeList(); // Perform an asynchronous request to get the matching city // and state. postRequest.url = "getCityAndState.asp"; postRequest.successCallback = cityStateCallback; postRequest.post("zipCode=" + zipCode); }
Again, the callback function is simplified since it just has to process the data returned.
function cityStateCallback(httpRequest) { // Assume no match was found. var statusText = "ZIP Code not found." // Fill in the city and state fields, if available. try { var data = httpRequest.responseText.split(","); if (data.length == 2) { document.forms[0].elements["city"].value = data[0]; document.forms[0].elements["state"].value = data[1]; statusText = "ZIP Code found." } } catch (ex) {} // Update the status message. setStatusText(statusText); }
Note that in this demo, we use one HttpRequest object to handle both lookup functions. We could have used to separate objects and allowed both to run at the same time but since the two functions are somewhat contradictory, it's best to use just the one. Because the HttpRequest object automatically aborts an active request before starting a new one, using just one means we don't have to worry about one request interfering with the other. In other situations, however, it may be acceptable to make concurrent requests using multiple instances of the object.
Conclusion
AJAX is an extension of DHTML programming, adding the capability to dynamically send and retrieve data from the web server in response to user actions.
The XmlHttpRequest object provides this functionality. Although its usage may not be intuitive, we've shown how it can be extended via a user-defined JavaScript object to hide the nuances of making asynchronous HTTP requests, handling errors and obtaining response data. The end result is a relatively easy to use interface that lets you concentrate on what you can do with it rather than how to do it.