Article

Home » Client-side Coding » JavaScript & Ajax Tutorials » AJAX: Usable Interactivity with Remote Scripting
SitePoint Feature Article

About the Author

Cameron Adams

author_cameronadams Cameron has been adding to the Internet for over seven years and now runs his own design and development business: www.themaninblue.com. He likes to combine the aesthetic with the technological on his Weblog, which contains equal parts of JavaScript, design and CSS.

View all articles by Cameron Adams...

AJAX: Usable Interactivity with Remote Scripting

By Cameron Adams

July 13th, 2005

Reader Rating: 9

Page: 1 2 3 Next

If your bookmarks contain even one Web development blog, you'll undoubtedly know that remote scripting is being touted as the new "future of the Web".

Although I get the feeling that a few people are perhaps a little over-excited about it, the recent release of numerous high-profile Web applications that use remote scripting has shown that there are definite advantages to utilising these techniques in the creation of seamless Web applications, and to augment Web page functionality.

This article aims to give you an introduction to the foundations of remote scripting, in particular, the emerging XMLHttpRequest protocol. We'll then walk through an example application that demonstrates how to implement that protocol, while creating a usable interface.

To begin, download the code archive, which contains all the files you'll need to create the working examples presented here.

What Is Remote Scripting?

Essentially, remote scripting allows client-side JavaScript to request data from a server without having to refresh the Web page. That's it. Everything else that goes into making a seamless Web application draws upon the established methods of manipulating the Document Object Model. This might be something as simple as creating a list of words to select from, as per Google Suggest. Or it could involve the creation of an entire interface for navigating and zooming map images, like that at map.search.ch.

However, along with the ability to create new Web experiences, remote scripting gives us the ability to make new unusable Web experiences. Remote scripting and seamless applications bring with them a host of problems from the desktop application design realm, making those same issues possible on the Web. It's your duty to ensure that your remote scripting interfaces address those issues, and give your users the best possible experience they can get.

Remote Scripting and Accessibility

As with any online application or Web page, we must always take into consideration the needs of users. Some users may have browsers that lack JavaScript capabilities, or, even if they can execute JavaScript, they may not have remote scripting capabilities.

It's a generally accepted best practice that JavaScript interaction and remote scripting functions add on to the basic functionality of Web-based content: the content must still be accessible and usable without these technologies. In the case of fully fledged Web applications, it may be acceptable to provide an entirely different system for users who don't have JavaScript or remote scripting capabilities. The team at GMail recently implemented a non-JavaScript, alternative interface to the service.

Remote Scripting Using XMLHttpRequest

Although XMLHttpRequest is not a public standard, most modern browsers implement it consistently, and it's well on its way to becoming a de facto standard for JavaScript data retrieval. Internet Explorer 5 for Windows, Mozilla 1.0, Safari 1.2 and the upcoming version 8.0 of Opera all introduce XMLHttpRequest as an available object.

The Internet Explorer XMLHttpRequest API is available for download.

You can also download the Mozilla documentation.

If you require support for browsers that are older than these, methods using iframes provide a viable solution; however, coding for these browsers will also limit your ability to utilize standard JavaScript DOM methods. This article will focus on the more contemporary XMLHttpRequest method.

Creating an XMLHttpRequest Object

For any browser, except Internet Explorer, we can create an XMLHttpRequest object like this:

var requester = new XMLHttpRequest();

However, in Internet Explorer, XMLHttpRequest is implemented as an ActiveX object. For IE, an object is created like this:

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

Note: this also means that if users have ActiveX objects disabled in Internet Explorer, they will be unable to use XMLHttpRequest even if JavaScript is enabled.

To cope with the differences in object creation syntax used by these browsers, it's best to use a try/catch structure to automatically provide you with the correct object, or return an error if the XMLHttpRequest object is not available:

try
{
 var requester = new XMLHttpRequest();
}
catch (error)
{
 try
 {
   var requester = new ActiveXObject("Microsoft.XMLHTTP");
 }
 catch (error)
 {
   return false;
 }
}

Thankfully, the difference between implementations ends there, and all subsequent method calls to the XMLHttpRequest object can be performed irrespective of which browser the script's running in.

Transporting Data using an XMLHttpRequest Object

Once an XMLHttpRequest object has been created, we must call two separate methods in order to get it to retrieve data from a server.

open() initialises the connection we wish to make, and takes two arguments, with several optionals. The first argument is the type of request we want to send; the second argument identifies the location from which we wish to request data. For instance, if we wanted to use a GET request to access feed.xml at the root of our server, we'd initialise the XMLHttpRequest object like this:

requester.open("GET", "/feed.xml");

The URL can be either relative or absolute, but due to cross-domain security concerns, the target must reside on the same domain as the page that requests it.

The open() method also takes an optional third boolean argument that specifies whether the request is made asynchronously (true, the default) or synchronously (false). With a synchronous request, the browser will freeze, disallowing any user interaction, until the object has completed. An asynchronous request occurs in the background, allowing other scripts to run and letting the user continue to access their browser. It's recommended that you use asynchronous requests; otherwise, we run the risk of a user's browser locking up while they wait for a request that went awry. open()'s optional fourth and fifth arguments are a username and password for authentication when accessing a password-protected URL.

Once open() has been used to initialise a connection, the send() method activates the connection and makes the request. send() takes one argument, allowing us to send extra data, such as CGI variables, along with the call. Internet Explorer treats it as optional, but Mozilla will return an error if no value is passed, so it's safest to call it using:

requester.send(null);

To send CGI variables using the GET request method, we have to hardcode the variables into the open() URL:

requester.open("GET", "/query.cgi?name=Bob&email=bob@example.com");
requester.send(null);

To send CGI variables using the POST request method, the CGI variables can be passed to the send() method like so:

requester.open("POST", "/query.cgi");
requester.send("name=Bob&email=bob@example.com");

Once we've called send(), XMLHttpRequest will contact the server and retrieve the data we requested; however, this process takes an indeterminate amount of time. In order to find out when the object has finished retrieving data, we must use an event listener. In the case of an XMLHttpRequest object, we need to listen for changes in its readyState variable. This variable specifies the status of the object's connection, and can be any of the following:

  • 0 – Uninitialised
  • 1 – Loading
  • 2 – Loaded
  • 3 – Interactive
  • 4 – Completed

Changes in the readyState variable can be monitored using a special onreadystatechange listener, so we'll need to set up a function to handle the event when the readyState is changed:

requester.onreadystatechange = stateHandler;

readyState increments from 0 to 4, and the onreadystatechange event is triggered for each increment, but we really only want to know when the connection has completed (4), so our handling function needs to realise this. Upon the connection's completion, we also have to check whether the XMLHttpRequest object successfully retrieved the data, or was given an error code, such as 404: "Page not found". This can be determined from the object's status property, which contains an integer code. "200" denotes a successful completion, but this value can be any of the HTTP codes that servers may return. If the request was not successful, we must specify a course of action for our program:

function stateHandler()
{
 if (requester.readyState == 4)
 {
 if (requester.status == 200)
 {
   success();
 }
 else
 {
   failure();
 }
}

return true;
}

Even though the XMLHttpRequest object allows us to call the open() method multiple times, each object can really only be used for one call, as the onreadystatechange event doesn't update again once readyState changes to "4" (in Mozilla). Therefore, we have to create a new XMLHttpRequest object every time we want to make a remote call.

Parsing the Data in an XMLHttpRequest Object

If we've made a successful request, two properties of the XMLHttpRequest object may contain data:

  • responseXML stores a DOM-structured object of any XML data that was retrieved by the object. This object is navigable using the standard JavaScript DOM access methods and properties, such as getElementsByTagName(), childNodes[ ] and parentNode.
  • responseText stores the data as one complete string. If the content type of the data supplied by the server was text/plain or text/html, then this is the only property that will contain data. A copy of any text/xml data will be flattened and placed here as an alternative to responseXML.

Depending upon the complexity of the data, it may be easier to return data simply as a plain text string, thereby making the XML in XMLHttpRequest redundant. However, for more complex data types, you'll probably want to use an XML format, such as this:

<?xml version="1.0" ?>
<user>
       <name>John Smith</name>
       <email>john@smith.com</email>
</user>

We are able to access different parts of the data using standard DOM access methods. Remember that data contained between tags is considered to represent child text nodes of the parent, so we have to take that extra layer of structure into account when we retrieve the data:

var nameNode = requester.responseXML.getElementsByTagName("name")[0];
var nameTextNode = nameNode.childNodes[0];
var name = nameTextNode.nodeValue;

We must also be careful about whitespace: indenting values in the XML file may produce unwanted whitespace in the value, or add additional text nodes.

Once we've parsed the data from the XMLHttpRequest object, we're free to change, delete and write it onto our Web page as we see fit!

An Example Remote Scripting Application

In order to demonstrate how to use the XMLHttpRequest protocol inside a remote scripting application, I've created a simple, one-page example. It assumes that JavaScript and XMLHttpRequest are available in order to make the code more readable, but in any real-world application, you should always check that XMLHttpRequest is available and have a fallback (i.e. normal form submission) where it is not.

The example application will allow the user to send a free ecard to a friend's email address. To do this, the user has first to enter a receipt number, which they received when they purchased goods previously, and which has since been stored in the database of ExampleCo. Then, the user must complete the remaining fields before the ecard is sent, entering the recipient's email address, the message, and the graphic image that will be used for the card:

1482_figure1

Remote scripting is used for three actions in this example, to:

  • Minimise the waiting time for validation of the receipt number
  • Automate the entry of retrieved data into the form once a receipt number has been validated
  • Re-write the contents of the page once the ecard has been sent successfully

Along with these actions, the example contains JavaScript, which validates the other form fields before submission, and allows the user to select an ecard graphic.

The example has been created in two separate versions. The first of these versions demonstrates the implementation of the XMLHttpRequest protocol inside an application, but it contains several less-than-desirable usability problems. These problems are tackled in the second example, which aims to highlight some of the issues that can be encountered as you move from a page-based application model towards a more dynamic and interactive environment.

If you liked this article, share the love:
Print-Friendly Version Suggest an Article

Sponsored Links