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).

______________________

Webinar
One Click, Universal Protection: Implementing Centralized Security Policies on Linux Systems

As Linux continues to play an ever increasing role in corporate data centers and institutions, ensuring the integrity and protection of these systems must be a priority. With 60% of the world's websites and an increasing share of organization's mission-critical workloads running on Linux, failing to stop malware and other advanced threats on Linux can increasingly impact an organization's reputation and bottom line.

Learn More

Sponsored by Bit9

Webinar
Linux Backup and Recovery Webinar

Most companies incorporate backup procedures for critical data, which can be restored quickly if a loss occurs. However, fewer companies are prepared for catastrophic system failures, in which they lose all data, the entire operating system, applications, settings, patches and more, reducing their system(s) to “bare metal.” After all, before data can be restored to a system, there must be a system to restore it to.

In this one hour webinar, learn how to enhance your existing backup strategies for better disaster recovery preparedness using Storix System Backup Administrator (SBAdmin), a highly flexible bare-metal recovery solution for UNIX and Linux systems.

Learn More

Sponsored by Storix