At the Forge - Beginning Ajax

How to put the A (asynchronous) in Ajax.

Many programmers, myself included, have long seen JavaScript as a way to change the appearance of a page of HTML dynamically or to perform relatively minor tasks, such as checking the validity of a form. In the past year, however, JavaScript has emerged as a major force for application developers, providing the infrastructure for so-called Ajax applications.

Before JavaScript, there was a one-to-one correspondence between user actions and the display of HTML pages. If a user clicked on a link, the currently displayed page disappeared and was replaced with another page of HTML. If a user submitted an HTML form, the contents of that form were submitted to a program on the Web server, and the content of the server's response was then displayed in the browser, replacing its predecessor. In traditional Web applications, server-side programs handle the bulk of user input and also build any dynamically generated Web pages the user might see.

Ajax applications redistribute the load, putting a greater emphasis on client-side JavaScript. In an Ajax application, many server-side programs do indeed produce complete pages of HTML, which are then displayed in their entirety in a Web browser. But many other server-side programs produce small snippets of XML-formatted data. This data is both requested and used by client-side JavaScript to modify and update the current HTML page without having to refresh or replace it. Using Web standards, such as the DOM (document object model) and CSS (cascading stylesheets), Ajax applications can approach the usability, friendliness and instant feedback that people expect from desktop applications.

This month, we continue exploring client-side JavaScript and Ajax, which we began during the past few months. Last month's column looked at a user-registration application for a Web site. Although the actual registration took place in a server-side program, we looked at ways in which we could provide an Ajax-style warning for registering users who wanted a user name that was already taken. Sure, we could have the server-side registration program check to see whether the user name had been taken already, but that would require refreshing the page, which also requires a delay.

The solution we implemented last month was fine from the user's perspective (especially if the user has somewhat Spartan tastes in design), but it solved the problem in a very non-Ajax way—by hard-coding the user names in a JavaScript array and then looking for the desired new user name in that array. This approach has a number of large problems associated with it, starting with the fact that the full list of user names is available to anyone looking at the HTML source and ending with the fact that the array will become unwieldy and cumbersome over time, taking an increasingly long time to download and search through as the number of registered users grows.

We can avoid these problems by using an Ajax-style solution. Rather than hard-code the list of user names in the JavaScript, and instead of having the server-side program produce a full list of user names, perhaps we could simply send a request to the server, checking to see if the requested user name is already taken. This will result in relatively fast download and reaction times, in a cleaner application design and in an extensible application.

This month, we take the Ajax plunge, modifying the server- and client-side programs we wrote last month to retrieve user names via an asynchronous request from the server. In producing this application, we will see how relatively straightforward it can be to create an Ajax application or to integrate Ajax functionality into a traditional Web application. By the end of this article, you should understand how to create the client and server sides of an Ajax application.

Making an Ajax Call

The technology that makes much of Ajax possible is JavaScript's XMLHttpRequest object. Using this object, a JavaScript function can make HTTP requests to a server and act on the results. (For security reasons, HTTP requests made by XMLHttpRequest must be sent to the server from which the current Web page was loaded.) The HTTP request may use either the GET or POST method, the latter of which allows us to send arbitrarily long, complex content to the server.

Most interesting, and at the core of many Ajax paradigms, is the fact that XMLHttpRequest may make its HTTP requests synchronously (forcing the browser to wait until the response has been completely received) or asynchronously (allowing the user to continue to use the browser window as it downloads additional information). Ajax applications typically use asynchronous calls. This allows different parts of the Web page to be updated and modified independently of one other, potentially responding simultaneously to multiple user inputs.

Ideally, we would be able to create an instance of XMLHttpRequest with the following JavaScript code:

var xhr = new XMLHttpRequest();

Unfortunately, life isn't that simple. This is because many people use Internet Explorer as their primary browser. IE does not have a native XMLHttpRequest object, and thus it cannot be instantiated in this way. Rather, it must be instantiated as:

var xhr = new ActiveXObject("Msxml2.XMLHTTP");

But wait! There are also some IE versions that require a slightly different syntax:

var xhr = new ActiveXObject("Microsoft.XMLHTTP");

How are we going to handle these three different ways of instantiating XMLHttpObject? One way is to use server-side browser detection. It is also possible to use client-side browser detection. But the most elegant method I have seen to date comes from Ajax Design Patterns, a new book by Michael Mahemoff (published by O'Reilly Media). Mahemoff uses JavaScript's exception-handling system to try each of these in turn until it works. By wrapping our three different instantiation methods in a function, and then assigning the value of our xhr variable to whatever the function returns, we can give our application cross-platform compatibility:

function getXMLHttpRequest () {
try { return new ActiveXObject("Msxml2.XMLHTTP"); } catch(e) {};
try { return new ActiveXObject("Microsoft.XMLHTTP"); } catch(e) {}
try { return new XMLHttpRequest(); } catch(e) {};
    return null;
}

var xhr = getXMLHttpRequest();

After executing the above code, we can be sure that xhr is either null (indicating that all attempts to instantiate XMLHttpRequest failed) or contains a valid instance of XMLHttpRequest. Once instantiated, XMLHttpRequest is compatible across browsers and platforms. The same methods thus will apply for all systems.

The most common method to call on xhr is open, which tells the object to send an HTTP request to a particular URL on the originating server. A call to xhr.open looks like this:

xhr.open("GET", "foo.html", true);

The first parameter (GET) tells xhr.open that we want to use the HTTP GET method. The second parameter names the URL that we want to retrieve; notice that because we must connect to the originating server, the initial protocol and hostname part of the URL is missing. The third parameter indicates whether the call is asynchronous (true) or synchronous (false). Almost all Ajax applications pass true, as this means that the browser doesn't freeze up while it is waiting for the HTTP response. This ability to make asynchronous HTTP requests is central to the magic of Ajax. Because the HTTP request doesn't affect the user interface and is handled in the background, the Web application feels more like a desktop application.

The call to xhr.open() does not actually send the HTTP request. Rather, it sets up the object so that when the request is sent, it uses the specified request method and parameters. To send the request to the server, we use:

xhr.send(null);

XMLHttpRequest does not return the HTTP response whoever calls xhr.send(). This is because we are using XMLHttpRequest asynchronously, as specified with the true value to xhr.open(). We cannot predict whether we will get results in half a second, five seconds, one minute or ten hours.

Instead, we tell JavaScript to invoke a function when it receives the HTTP response. This function will be responsible for reading and parsing the response and then taking appropriate action. One simple version of the function, which I have called parseHttpResponse, is as follows:

function parseHttpResponse() {
    alert("entered parseHttpResponse");
    if (xhr.readyState == 4) {
        alert("readystate == 4");
        if (xhr.status == 200) {
            alert(xhr.responseText);
        }
        else
        {
            alert("xhr.status == " + xhr.status);
        }
    }
}

parseHttpResponse is called when the HTTP response to our Ajax request comes in. However, we have to make sure that the response contents have completely arrived, which we do by monitoring xhr.readyState. When that equals 4, we know that xhr has received the complete response. Our next step is then to check that the response had an HTTP “OK” (200) code. After all, it is always possible that we got a 404 (“file missing”) error from the server, or that we failed to connect to the server at all.

To tell JavaScript we want to invoke parseHttpResponse when our HTTP request returns, we set the onreadystatechange attribute in our XMLHttpRequest object:

xhr.onreadystatechange = parseHttpResponse;

Finally, after we can be sure that we have received the response and that all is well, we can grab the text of the response with the xhr.responseText method. Our XMLHttpRequest can return its response either as a text string (as here) or as an XML document. In the latter case, we then can use the DOM to navigate through it, much as we would do with a Web page.

Of course, an actual Ajax application would not issue an alert at every step of its execution and would probably do something more useful—perhaps changing some text, adding or removing some nodes from the document tree or changing part of the document's stylesheet. Nevertheless, you can see this code in action in Listing 1 (ajax-test.html).

______________________

White Paper
Linux Management with Red Hat Satellite: Measuring Business Impact and ROI

Linux has become a key foundation for supporting today's rapidly growing IT environments. Linux is being used to deploy business applications and databases, trading on its reputation as a low-cost operating environment. For many IT organizations, Linux is a mainstay for deploying Web servers and has evolved from handling basic file, print, and utility workloads to running mission-critical applications and databases, physically, virtually, and in the cloud. As Linux grows in importance in terms of value to the business, managing Linux environments to high standards of service quality — availability, security, and performance — becomes an essential requirement for business success.

Learn More

Sponsored by Red Hat

White Paper
Private PaaS for the Agile Enterprise

If you already use virtualized infrastructure, you are well on your way to leveraging the power of the cloud. Virtualization offers the promise of limitless resources, but how do you manage that scalability when your DevOps team doesn’t scale? In today’s hypercompetitive markets, fast results can make a difference between leading the pack vs. obsolescence. Organizations need more benefits from cloud computing than just raw resources. They need agility, flexibility, convenience, ROI, and control.

Stackato private Platform-as-a-Service technology from ActiveState extends your private cloud infrastructure by creating a private PaaS to provide on-demand availability, flexibility, control, and ultimately, faster time-to-market for your enterprise.

Learn More

Sponsored by ActiveState