Previous Page
Next Page

Reading Server Data

We begin our exploration of Ajax with the basics: using the XMLHttpRequest object to retrieve and display information from a server.

To get the job done, we'll use Scripts 15.1 (HTML) and 15.2 (JavaScript). There are two possible files that can be read: the XML file that is shown in Script 15.3 and the plain text file that is Script 15.4.

Script 15.1. The HTML for the text and XML file request example.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN">
<html>
<head>
     <title>My First Ajax Script</title>
     <script src="script01.js" type="text/javascript" language="Javascript">
     </script>
</head>
<body>
     <p><a id="makeTextRequest" href="gAddress.txt">Request a text file</a><br />
     <a id="makeXMLRequest" href="us-states.xml">Request an XML file</a></p>
     <div id="updateArea">&nbsp;</div>
</body>
</html>

Script 15.2. This JavaScript gets the files from the server.

window.onload = initAll;

var xhr = false;
function initAll() {
     document.getElementById("makeTextRequest"). onclick = getNewFile;
     document.getElementById("makeXMLRequest"). onclick = getNewFile;
}

function getNewFile() {
     makeRequest(this.href);
     return false;
}

function makeRequest(url) {
     if (window.XMLHttpRequest) {
        xhr = new XMLHttpRequest();
     }

     else {
        if (window.ActiveXObject) {
           try {
              xhr = new ActiveXObject ("Microsoft.XMLHTTP");
           }
           catch (e) { }
        }
     }

     if (xhr) {
        xhr.onreadystatechange = showContents;
        xhr.open("GET", url, true);
        xhr.send(null);
     }
     else {
        document.getElementById("updateArea"). innerHTML = "Sorry, but I couldn't create
 an XMLHttpRequest";
     }
}

function showContents() {
     if (xhr.readyState == 4) {
        if (xhr.status == 200) {
           var outMsg = (xhr.responseXML && xhr.responseXML.contentType== "text/xml") ? 
xhr.responseXML. getElementsByTagName("choices") [0].textContent: xhr.responseText;
        }
        else {
           var outMsg = "There was a problem with the request " + xhr.status;
        }
        document.getElementById("updateArea"). innerHTML = outMsg;
     }
}

Script 15.3. This is the XML file that is requested.

<?xml version="1.0"?>
<choices xml:lang="EN">
     <item><label>Alabama</label><value>AL</value></item>
     <item><label>Alaska</label><value>AK</value></item>
     <item><label>Arizona</label><value>AZ</value></item>
     <item><label>Arkansas</label><value>AR</value></item>
     <item><label>California</label><value>CA</value></item>
     <item><label>Colorado</label><value>CO</value></item>
     <item><label>Connecticut</label><value>CT</value></item>
     <item><label>Delaware</label><value>DE</value></item>
     <item><label>Florida</label><value>FL</value></item>
     <item><label>Georgia</label><value>GA</value></item>
     <item><label>Hawaii</label><value>HI</value></item>
     <item><label>Idaho</label><value>ID</value></item>
     <item><label>Illinois</label><value>IL</value></item>
     <item><label>Indiana</label><value>IN</value></item>
     <item><label>Iowa</label><value>IA</value></item>
     <item><label>Kansas</label><value>KS</value></item>
     <item><label>Kentucky</label><value>KY</value></item>
     <item><label>Louisiana</label><value>LA</value></item>
     <item><label>Maine</label><value>ME</value></item>
     <item><label>Maryland</label><value>MD</value></item>
     <item><label>Massachusetts</label><value>MA</value></item>
     <item><label>Michigan</label><value>MI</value></item>
     <item><label>Minnesota</label><value>MN</value></item>
     <item><label>Mississippi</label><value>MS</value></item>
     <item><label>Missouri</label><value>MO</value></item>
     <item><label>Montana</label><value>MT</value></item>
     <item><label>Nebraska</label><value>NE</value></item>
     <item><label>Nevada</label><value>NV</value></item>
     <item><label>New Hampshire</label><value>NH</value></item>
     <item><label>New Jersey</label><value>NJ</value></item>
     <item><label>New Mexico</label><value>NM</value></item>
     <item><label>New York</label><value>NY</value></item>
     <item><label>North Carolina</label><value>NC</value></item>
     <item><label>North Dakota</label><value>ND</value></item>
     <item><label>Ohio</label><value>OH</value></item>
     <item><label>Oklahoma</label><value>OK</value></item>
     <item><label>Oregon</label><value>OR</value></item>
     <item><label>Pennsylvania</label><value>PA</value></item>
     <item><label>Rhode Island</label><value>RI</value></item>
     <item><label>South Carolina</label><value>SC</value></item>
     <item><label>South Dakota</label><value>SD</value></item>
     <item><label>Tennessee</label><value>TN</value></item>
     <item><label>Texas</label><value>TX</value></item>
     <item><label>Utah</label><value> UT</value></item>
     <item><label>Vermont</label><value> VT</value></item>
     <item><label>Virginia</label><value> VA</value></item>
     <item><label>Washington</label><value> WA</value></item>
     <item><label>West Virginia</label><value> WV</value></item>
     <item><label>Wisconsin</label><value> WI</value></item>
     <item><label>Wyoming</label><value> WY</value></item>
</choices>

Script 15.4. The requested text file.

Four score and seven years ago our fathers
brought forth on this continent, a new nation,
conceived in Liberty, and dedicated to the
proposition that all men are created equal.
Now we are engaged in a great civil war, testing
whether that nation, or any nation so conceived
and so dedicated, can long endure. We are met on
a great battle-field of that war. We have come
to dedicate a portion of that field, as a final
resting place for those who here gave their lives
that that nation might live. It is altogether
fitting and proper that we should do this.

But, in a larger sense, we can not dedicate --
we can not consecrate -- we can not hallow --
this ground. The brave men, living and dead,
who struggled here, have consecrated it, far
above our poor power to add or detract. The
world will little note, nor long remember what
we say here, but it can never forget what they
did here. It is for us the living, rather, to
be dedicated here to the unfinished work
which they who fought here have thus far so
nobly advanced. It is rather for us to be here
dedicated to the great task remaining before
us -- that from these honored dead we take
increased devotion to that cause for which they
gave the last full measure of devotion -- that
we here highly resolve that these dead shall
not have died in vain -- that this nation, under
God, shall have a new birth of freedom -- and
that government of the people, by the people,
for the people, shall not perish from the earth.

To request server data:

1.
var xhr = false;



In Script 15.2, the xHR variable is one that you'll be seeing a lot of in this chapter. It's an XMLHttpRequest object (or it will be later, after it's initialized). At this point, we just need to create it outside any functions in order to make it globally available.

2.
function initAll() {
  document.getElementById ("makeTextRequest").onclick = getNewFile;
  document.getElementById ("makeXMLRequest").onclick = getNewFile;
}



When the page is first loaded, it knows to call the initAll() function. Here, we set two onclick handlers so that when a user clicks either of the links, the getNewFile() function is triggered.

3.
function getNewFile() {
  makeRequest(this.href);
  return false;
}



Someone's clicked a link, so it's time to do something. Here, that something is to call makeRequest()but that function needs to know which file was requested. Thankfully, we know that that information is tucked away in this.href, so we can pass it along. When we come back, we know we're done, so we return a value of false, telling the browser that no, we don't really want to load up a new Web page.

4.
if (window.XMLHttpRequest) {
  xhr = new XMLHttpRequest();
}



Now, we're inside makeRequest(), and it's here that things get interesting. Modern browsers support a native XMLHttpRequest object as a property of window. So, we check to see if that property exists, and if it does, we create a new XMLHttpRequest object.

5.
if (window.ActiveXObject) {
  try {
     xhr = new ActiveXObject ("Microsoft.XMLHTTP");
  }
  catch (e) { }
}



However, there's a browser that supports XMLHttpRequest that doesn't have a native version of the object, and that's Microsoft Internet Explorer (versions 5.5 and 6). In that case, we have to check to see if the browser supports ActiveX. If it does, we then check (using a try/catch error check) to see if we can create an XMLHttpRequest object based on ActiveX. If we can, great.

6.
if (xhr) {
  xhr.onreadystatechange = showContents;
  xhr.open("GET", url, true);
  xhr.send(null);
}



Either way, we should have a new xhr object, and if we do, we need to do something (in fact, three somethings) with it. Here are the three things that we always do with xhr:

  • Set the xhr's onreadystatechange event handler. Any time the xhr.readyState property changes its value, this handler is triggered.

  • We call open() and pass in three parameters: an HTTP request method (e.g., "GET", "POST", or "HEAD"), a URL to a file on the server, and a Boolean telling the server if the request is asynchronous (that is, if we're sitting around waiting for it).

  • And finally, we send() the request we just created. If we were requesting a POST, the parameters would be passed here.

7.
else {
  document.getElementById ("updateArea").innerHTML = "Sorry, but I couldn't create an
 XMLHttpRequest";
}



If we end up here, we couldn't create an XMLHttpRequest for some reason, and there's nothing else that can be done.

8.
if (xhr.readyState == 4) {
  if (xhr.status == 200) {



Now we're down in the showContents() function. The readyState property can have one of several values (see Table 15.1), and every time the server changes its value, the showContents() function is triggered. However, we don't actually want to do anything (at least not here) until the request is finished, so we start off by checking to see if readyState is 4. If it is, we're good to go, and we can check to see what the request returned.

Table 15.1. readyState Property Values

Value

What It Means

0

Uninitialized; object contains no data

1

Loading; object is currently loading its data

2

Loaded; object has finished loading its data

3

Interactive; user may interact with the object even though it is not fully loaded

4

Complete; object has finished initializing


The first thing to check is the request's status, which will be a result code returned by the server (servers routinely return these codes behind the scenes for every file served, although browsers only show them to you if there's an error). A status code of 200 means that everything's fine. The status here is the same status that is returned by any server call; for instance, if you ask for a file that doesn't exist you'll get a 404 error from the Web server.

9.
var outMsg = (xhr.responseXML && xhr.responseXML.contentType =="text/xml") ? xhr
.responseXML. getElementsByTagName("choices")[0]. textContent: xhr.responseText;



If we're here, that means that everything is fine, and we want to look at what the server actually gave us. There were two different types of files we could be reading, so we need to check what type of data we got back. The responseXML property contains the data if it's XML. If the contentType property (which can also be referred to as the MIME type) contains "text/xml", then we know that we've got a properly formatted DOM object back, and we can use commands we've seen before (such as getElementsByTagName()) to traverse its nodes. But here, we're just trying to see if it worked, so we'll take everything and dump it all into outMsg.

If what we got back isn't valid XML, then it's our text file. In that case, we want to put xHR's responseText property into outMsg.

10.
else {
  var outMsg = "There was a problem with the request " + xhr.status;
}



If what we got back had a status other than 200, we've got a problem, so we set outMsg to say that and append the status error so we can try to figure out what the problem is.

11.
document.getElementById ("updateArea").innerHTML = outMsg;



And finally, we take outMsg and dump it onto the screen, as shown in Figure 15.2.

Figure 15.2. By clicking the appropriate link, you can fetch either a text file of the Gettysburg Address (top) or an XML file of U.S. states and their abbreviations (bottom).


Tips

  • Because of the way that Ajax works, when you are doing your development and testing, the files that you're reading must reside on a server; they can't just be local files.

  • Back in step 5, we said that IE 5.5 and 6 used an ActiveX control to create the XMLHttpRequest object. Thankfully, IE7 has a native object so that's no longer required. However, this means that you always have to check for the existence of a native object firstif you check for window.ActiveXObject first, that will be true for IE7, and then you'll be going down the wrong path. A considerable amount of older, pre-IE7 Ajax code has this problem.

  • If it matters deeply to your code which version of Microsoft's ActiveX object you actually get, here's a code snippet for you to use instead:

    if (window.ActiveXObject) {
      try {
        xhr = new ActiveXObject ("Msxml2.XMLHTTP");
      }
      catch (e) {
        try {
          xhr = new ActiveXObject ("Microsoft.XMLHTTP");
        }
        catch (e) { }
      }
    

    This approach attempts to use the IE6 version (Msxml2.XMLHTTP) of the XMLHttpRequest object first and only falls back to the older version if it can't find it. However, the Microsoft.XMLHTTP version should always give you the latest version available on the PC, so we'll just be using that in this chapterbecause eventually, the older code will be going away.


Tips

  • One drawback of Ajax calls is that they can be cached; that is, it looks like your application is contacting the server and getting new data, but it's really just looking at stuff it read previously. If that's the case, setting the headers of the request can help. Adding one or more of these can help force recalcitrant servers to fork over the goods:

    xhr.setRequestHeader ("If-Modified-Since", "Wed, 15 Jan 1995 01:00:00 GMT");
    xhr.setRequestHeader ("Cache-Control","no-cache");
    xhr.setRequestHeader ("Cache-Control", "must-revalidate");
    xhr.setRequestHeader ("Cache-Control","no-store");
    xhr.setRequestHeader ("Pragma","no-cache");
    xhr.setRequestHeader ("Expires","0");
    
  • You can force the call to return XML data by overriding the MIME type:

    xhr.overrideMimeType("text/xml");
    

    However, this may cause problems with certain browsers and configurations, so use it with care.



Previous Page
Next Page