-->
Bookmark and Share

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.

It should be noted that, as a security measure, the XMLHttpRequest object can only be used to access a URL with the same host as the page in which it is used.*

For example, a page loaded from http://www.example.net/sample/client.html cannot use XMLHttRequest to pull data from http://www.test.com/scripts/server.php.

Additionally, if the page is also accessible using http://example.net/sample/client.html (with out the www), the XMLHttpRequest object must use example.net rather than www.example.net. Fortunately, you can use relative URLs, such at /sample/server.php to avoid the problem.

*In IE, you may be able to access URLs with a different host depending on the individual browser's security settings. But even if it is allowed, the browser will display a pop-up dialog warning the user of the security risk and prompting him or her to permit or deny access.

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:

ZIP Code:
City: State:
 

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:

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.