Sams Teach Yourself Web Publishing with HTML and CSS in One Hour a Day (5th Edition)

The aspect that dynamic HTML adds to regular HTML is the capability to manipulate elements on a page without sending requests back to the server to reload the entire page. You can hide or display items or even change their contents using JavaScript, Cascading Style Sheets, and the DOM. Using a JavaScript feature called XmlHttpRequest, you can take dynamic HTML one step further by requesting data from the server and incorporating it on the page without reloading the entire page. The technique is commonly referred to as AJAX, which is short for Asynchronous JavaScript + XML, and was originally described that way in an article by Jesse James Garrett of Adaptive Path.

So far, you've seen how JavaScript can be used to manipulate objects that are already present on a web page. With XmlHttpRequest, you can write JavaScript that actually makes a call back to the server, submitting and receiving data. Let's say you have a web page that displays baseball scores and you want the scores to be updated automatically to show the progress of the games. Ordinarily, you would automatically refresh the entire page every so often, or the user could reload the page using her browser to get the most recent scores. Using AJAX, you could add some JavaScript to the page that goes back to the server and gets the latest scores periodically, updating that information while leaving the rest of the page intact.

This provides users with a more seamless experience and saves bandwidth on your server, as the only parts that are downloaded are those that actually change. Dynamic HTML took web programmers most of the way toward creating web applications that work like desktop applications, and AJAX closes that gap even further.

How AJAX Works

AJAX depends on XmlHttpRequest. It was originally added to Microsoft Internet Explorer 5 as an ActiveX object, and has since been added to other browsers as a built-in JavaScript object. XmlHttpRequest will submit a request to a web server where the enclosing page is stored and return the results so that they can be used within the web page that made the XmlHttpRequest request. For security reasons, XmlHttpRequest only allows connections back to the web server where the page making the request is stored. In other words, if the page containing the XmlHttpRequest call is http://www.example.com/ajax.html, the browser could only submit requests to www.example.com.

Originally, XmlHttpRequest was intended to retrieve XML documents from the server, but in actuality it can retrieve any content as long as it's text. You can then incorporate the results into your page using the DHTML techniques you've already seen in this lesson. The first step in using XmlHttpRequest is creating the object. As I mentioned, Internet Explorer and other browsers handle XmlHttpRequest differently, so you have to use the capability detection features that I mentioned in the "Creating an Image Rollover" section in Lesson 13, "Using JavaScript in Your Pages," to properly create the object. This also enables you to avoid running into problems with browsers that don't support XmlHttpRequest at all. Here's the code:

if (window.XMLHttpRequest) { request = new XMLHttpRequest(); } else if (window.ActiveXObject) { request = new ActiveXObject("Microsoft.XMLHTTP"); }

First I check to see whether the browser provides XmlHttpRequest as a property of the window object. If it does (as browsers other than Internet Explorer do), I use the new keyword to create a new XmlHttpRequest object. If it does not, I check to see whether the browser supports the ActiveXObject property of window. Only Internet Explorer provides that feature, so if that check returns true, I can create an XmlHttpRequest object using ActiveX. The XmlHttpRequest object, once created, works the same in all browsers that support it. Here's how you use it to retrieve information from the server:

request.onreadystatechange = processReadyStateChange; request.open("POST", "remote.cgi", true); request.send("user=test");

I'll explain the first line last. The second line is used to specify the request method and URL for the connection. The first argument, "POST" in this example, is the request method to use. You can use "GET" or "POST" here, just as you can with the action attribute of the <form> element, as discussed in Lesson 10, "Designing Forms." The second argument is the URL. Since the request must go to the server where the page resides, you can just use a relative or absolute URL. There's no need to specify the server or protocol. In this case, the script would submit the request to remote.cgi in the same directory as the page containing this code. The final argument specifies whether the request should be asynchronous, and can be either TRue or false. You'll almost certainly want to use true here all the time. If you don't set the request to be asynchronous, the browser will wait for the request to complete before letting the user do anything.

Once you've created the connection, you need to send data over the connection using the send() method. If you use the GET method, you can just use an empty string ("") here. If you use POST, you'll include the actual data to send with the request as the argument to the send() method. Once again, think about the request as a form submission. You can gather data from form elements on the page and pass it to send() to submit the form via XmlHttpRequest rather than submitting the form itself. The data passed to send() should be encoded using URL encoding, as discussed in Lesson 10. After the data is submitted, your script has to handle the response from the server. That's where the first line comes in. In order to deal with the response, you have to register a function to handle events associated with XmlHttpRequest. You'd think that you could just write some JavaScript to handle the results and put it in your script after the line that sends the data to the server, but that's not how XmlHttpRequest works. Remember that I mentioned that XmlHttpRequest can be used asynchronously. What that means is that once the script sends the request, your code can go on and do other things while the browser deals with handling the results of the request.

The browser processes the response by generating events. If you want to do anything with the results, you have to register a function as the handler for those events. I discussed event handlers back in Lesson 12, and I showed how you can register a function to handle them by using attributes of elements using attributes such as onclick or onchange. For example, if you wanted to register a handler for an onclick event for a link, you could use this code:

<a href="/" onclick="linkClicked()">My link</a>

There's another way to register a handler as well, entirely within a script. If you wanted to use JavaScript to register the event handler for the link, which I helpfully gave the ID mylink, you could do it like this:

document.getElementById("mylink").onclick = linkClicked;

One thing to note is that I don't include the () after linkClicked in the JavaScript example. That's because I want to associate the function named linkClicked with that event. If I include the parentheses, the script will actually call the function and assign the results to the onclick property. I want the function itself in this case.

OK, back to XmlHttpRequest. As I mentioned, the browser handles the response by generating events. I specified the handler for those events on this line:

request.onreadystatechange = processReadyStateChange;

That line says that onreadystatechange events for the XmlHttpRequest object should be passed to a function named processReadyStateChange. When a response is being processed, it goes through multiple states before it's finished. Those states are listed in Table 15.2.

Table 15.2. XmlHttpRequest States

State

Description

0

The open() method has not been called.

1

The send() method has not been called.

2

The send() method has been called but the response headers have not been sent back.

3

Some response data has been received.

4

All of the response data has been received.

As you can probably guess, the states progress from 0 to 4. The function you associate with onreadystatechange is called every time the state advances. Here's what such a function looks like:

function onreadystatechange() { if (request.readyState == 4) { window.alert("Finished!"); } }

This function examines the readyState property of the XmlHttpRequest object to see what the current state of the request is. This function will be called once for every state change, but it doesn't do anything until state 4 is reached and the request is finished. At that point, it just displays an alert. Most handlers will be written like this because the only state you care about is state 4. In addition to readyState, XmlHttpRequest has other properties, which are listed in Table 15.3.

Table 15.3. XmlHttpRequest Properties

Property

Description

onreadystatechange

The function specified to handle state change events

readyState

The current ready state for the object

responseText

The response sent by the server

status

The HTTP status code of the response

statusText

The text message associated with the HTTP status

Once you've reached readyState 4, you can access the data passed back from the server via the responseText property and deal with it as you wish.

An AJAX Example

Now that you understand how AJAX works, here's an actual page that applies these principles. In this example, I've created a page that allows you to update the current temperature by pressing a button. I use XmlHttpRequest to retrieve the current temperature, and use JavaScript to replace a <div> on the page with data from the server. Here's the source code for the page:

Input

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" version="-//W3C//DTD XHTML 1.1//EN" xml:lang="en"> <head> <title>AJAX Example</title> <script language="JavaScript"> var request = false; function sendAjaxRequest(toUrl) { if (window.XMLHttpRequest) { request = new XMLHttpRequest(); } else if (window.ActiveXObject) { request = new ActiveXObject("Microsoft.XMLHTTP"); } if (request) { request.onreadystatechange = processReadyStateChange; request.open('GET', toUrl, true); request.send(""); } } function updateTemperature(msg) { var contentDiv = document.getElementById("ajaxTarget"); contentDiv.innerHTML = msg; } function getLatestTemp() { sendAjaxRequest("temp.txt"); } function processReadyStateChange() { if (request.readyState == 4) { if (request.status == 200) { updateTemperature(request.responseText); } else { alert("An error occurred."); } } } </script> </head> <body> <p> Current temperature: <div >90 degrees</div> </p> <p> <button onclick="getLatestTemp()">Update Temperature</button> </p> </body> </html>

The page appears in Figure 15.6.

Output

Figure 15.6. A page that uses AJAX to update the current temperature.

As you can see, the HTML section of the page is relatively short. All I have is a paragraph containing a <div> that contains the current temperature and another paragraph with a button that's used to update the temperature.

When the user clicks on the Update Temperature link, the function specified in the onclick handlergetLatestTemp()is called. That function contains a single line of code:

sendAjaxRequest("temp.txt");

This code just calls another function I wrote called sendAjaxRequest(). The argument is the URL for the content on the server. Ordinarily, the URL would point to a script that looks up the current temperature and transmits it back to the user. In this case, I have a text file on the server in the same directory as this page, and XmlHttpRequest is happy to retrieve it. The temp.txt file contains only the following text:

55 degrees

The code in the sendAjaxRequest() function should look familiar, since I just explained how it works. I create a new XmlHttpRequest object, assign a handler to the onreadystatechange event, open the connection, and send the request data. One thing you might notice is that I declare the request variable at the top level of my script rather than within my function. That's so that the variable is visible to the handler function as well as to the function where it is assigned.

I also test to make sure that my attempts to create the XmlHttpRequest object were successful prior to calling any of its methods. That way I won't produce errors in older browsers that don't support XmlHttpRequest. In this script, my event handling function does a lot more than the previous one. Here's the source:

function processReadyStateChange() { if (request.readyState == 4) { if (request.status == 200) { updateTemperature(request.responseText); } else { alert("An error occurred."); } } }

As in the preceding code, the script only cares about readyState 4. It ignores all of the state changes leading up to it. Once readyState 4 has been reached, it checks the status property of the XmlHttpRequest object. A status of 200 means that the request was successful and the script received the text it wants. If the status is anything else, an error message is displayed. If the request was successful then the script calls the updateTemperature function, passing in the response text as an argument.

In this case, temp.txt contains only the temperature to be displayed. More involved scripts could produce complex data structures represented as XML that the page has to unpack, but in this example I just want to take the result and display it. That's what updateTemperature() is for:

function updateTemperature(msg) { var contentDiv = document.getElementById("ajaxTarget"); contentDiv.innerHTML = msg; }

The code uses the getElementById() function to obtain a reference to the <div> on the page. It then replaces its current contents with the response text using the innerHTML property.

As I said before, a real-life example would be significantly more complex, but this example illustrates how AJAX is used in a simple case. Unlike the other examples you've seen in this book so far, you'd have to put both the HTML page and temp.txt file on a web server in order for the example to work. XmlHttpRequest relies on the HTTP protocol, and its security constraints require the target to be on the same server as the page making the call.

In Lesson 20, "Understanding Server-Side Processing," you'll learn how to write the kinds of scripts that are used to produce dynamic results for AJAX calls, unlike the static text file I provided for this example.

Be Careful with AJAX

AJAX is relatively new, and isn't supported in older browsers. As with dynamic HTML, you should make sure that your site is usable by people who can't take advantage of any of the features you provide using AJAX.

AJAX can also be used in such a way that it breaks well-established web browsing conventions, such as the Back button. You should use AJAX to supplement your site, and not to implement the entire thing. Your users will probably be used to the way that most other websites work, and using too much AJAX may confuse them.

Категории