XMLHttpRequest Call - Ajax Patterns

XMLHttpRequest Call

From Ajax Patterns

Evidence: 3/3

Tags: Call Callback Download Live Query Remoting RemoteScripting Upload XMLHttpRequest


shakopee homes

Contents

In A Blink

Diagram: Script in Web page issuing XMLHttpRequest Call to server

Goal Story

Reta's purchasing some items from a wholesaler's website. Each time she adds an item to the shopping cart, the website issues an XMLHttpRequest to save the latest cart contents. There's no form submission, so the item is added instantaneously, which saves Reta time as well as helping her understand what's going on.


Problem

How can the browser communicate with the server?


Forces

  • Ajax applications require browser-server communication. User-generated information must be uploaded and new server information must be downloaded.
  • Because Ajax applications should have a smooth and continuous feel, browser-server communication must be unobtrusive.
  • Because Ajax applications should be highly responsive, browser-server communication must be fast.
  • The conventional means of browser-server communication - hyperlinks and form submissions - are obtrusive and slow. They are obtrusive because they require a page refresh and slow because the response must contain an entire page.
  • The conventional means of browser-server communication - hyperlinks and form submissions - make browser-side programming tedious because they force page refreshes, which in turn resets Javascript state. Any state worth retaining must therefore be uploaded to the server and sent back to the browser.


Solution

Use XMLHttpRequest objects for browser-server communication. XMLHttpRequest is a JavaScript object capable of calling the server and capturing its response. Just like a browser or a command-line web client, an XMLHttpRequest issues standard HTTP requests and grabs responses. Note: The code shown throughout this demo is closely based on an online companion demo.

To begin with, let's think about where the call goes. An XMLHttpRequest calls on a particular URL, possibly passing in some arguments on the URL or the message body. But what sort of thing resides at the URL?

That sort of thing's not very useful for XMLHttpRequest Calls, which tend to be quite specific in nature, e.g. a query like "What's the user's account balance?" or a submission like "The user wants to trade 5000 units". There's no need to output an entire page of HTML because it's the script that will decide how to change the page, if at all. To the contrary, output should be small and easily parseable. Thus, input is constrained to the bare minimum required by the script, and the output is a concise return value, like "$20,510" or "Trade Accepted". In these patterns, I refer to a resource like this as a "web service", or simply, a "service". A collection of related web services is sometimes called a "web API". There are a number of patterns about Web Services, but the important thing here is that a web service has relatively simple input and output, more like that of a function call than a conventional web application.

Here's a one-liner "web service" that's running at Ajaxify, where all the demos reside:

  <?php
    echo $_GET["figure1"] + $_GET["figure2"];
  ?>

You can try the service by entering http://ajaxify.com/run/xmlHtttpRequestCall/sumGet.php?figure1=5&figure2=10 in your browser's address bar. The entire response will be "15".

Now the fun part: calling the service from JavaScript. In the following script, XMLHttpRequest achieves the same thing as the browser just did, but programatically.

 var xhReq = new XMLHttpRequest();
 xhReq.open("GET", "sumGet.phtml?figure1=5&figure2=10", false);
 xhReq.send(null);
 var serverResponse = xhReq.responseText;
 alert(serverResponse); // Shows "15"

The sequence begins by creating a new instance of XMLHttpRequest. xhReq.open() then prepares a call on the test service, sumGet.phtml (the code's running from the same path, so the domain and path need not be qualified). The "GET" signifies the request method to be used. The "false" option says the call is synchronous, meaning that the code will hang until a response comes back. The send command completes the request. Because the call is synchronous, the result is ready on the next line. The XMLHttpRequest object has saved the response from the server and you can access it with the responseText field.

The above example hopefully illustrates that the fundamental technology is pretty simple. However, be aware that it's a very basic usage, not yet fit for production. Fundamental questions remain:

  • How can XMLHttpRequests be created?
  • How does an asynchronous call work?
  • How does our code detect errors?
  • What if the service requires a "POST" or "PUT" request rather than a "GET"?
  • Which URLs can be accessed?
  • How can you deal with XML responses?
  • What's the API?

The following sections address these questions and show how the code above needs to be modified.

Creating XMLHttpRequest Objects

In most browsers, XMLHttpRequest is a standard JavaScript class, so you just create a new instance XMLHttpRequest. However, Microsoft were the inventors of XMLHttpRequest, and until IE7, IE only it offered as an ActiveX object. To make things even more fun, there are different versions of that object. The following code shows a factory function that works on all browsers that support XMLHttpRequest.

 function createXMLHttpRequest() {
   try { return new XMLHttpRequest(); } catch(e) {}
   try { return new ActiveXObject("Msxml2.XMLHTTP"); } catch (e) {}
   alert("XMLHttpRequest not supported");
   return null;
 }
 ...
 var xhReq = createXMLHttpRequest();

With a function like this, creation is easy for all browsers. The basic functionality and API are pretty consistent across browsers, but be sure to test carefully as there are a few subtle implementation differences in some browsers.

You can also reuse an XMLHttpRequest. Just make sure it's either delivered a full response or it's been reset using the abort() method described below.

Asynchronous Calls

Just above, I explained that under synchronous mode, "the code will hang until a response comes back". Hardened readers might have writhed uncomfortably at the thought. We all know that some requests take a long time to process, and some don't come back at all. Pity the web user who's stuck waiting for a server that's buried in an infinite loop.

In practice, XMLHttpRequest Calls should almost always be asynchronous. That means the browser and the user can continue working on other things while waiting for a response to come back. How will you know when the response is ready? The XMLHttpRequest's readyState always reflects the current point in the call's lifecycle. When the object is born, it's at 0. After open() has been called, it's 1. This continues until the response is back, at which point the value is 4.

So, to catch the response, you need to watch for a readyState of 4. That's an easy chore, because XMLHttpRequest fires readystatechange events. You can declare a callback function using the onreadystatechange field. The callback will then receive all state changes. The states below 4 aren't especially useful and are somewhat inconsistent across browser types anyway. So most of the time, all we're interested in is state 4, when the response is complete.

Based on all that, here's an asynchronous version of the code shown earlier.

 function onSumResponse() {
   if (xhReq.readyState != 4)  { return; }
   var serverResponse = xhReq.responseText;
   ...
 }
 ...
 var xhReq = createXMLHttpRequest();
 xhReq.open("GET", "sumGet.phtml?figure1=5&figure2=10", true);
 xhReq.onreadystatechange = onSumResponse;
 xhReq.send(null);

As shown, you declare the callback method in XMLHttpRequest's onreadystatechange property. In addition, the third argument of open() is now true. This argument is actually called the "asynchronous flag", which explains why we're now setting it to true. The callback function, "onSumResponse", is registered using onreadystatechange and contains a guard clause to ensure the readyState is 4 before any processing can occur. At that point, we have the full response in responseText.

JavaScript also supports "closures" - a form of anonymous function - which suggests a more concise boilerplate structure for asynchronous calls:

 var xhReq = createXMLHttpRequest();
 xhReq.open("get", "sumget.phtml?figure1xcxcxc=10&figure2=20", true);
 xhReq.onreadystatechange = function() {
   if (xhReq.readyState != 4)  { return; }
   var serverResponse = xhReq.responseText;
   ...
 };
 xhReq.send(null);

A warning: if you look carefully at the callback mechanism, you might notice the potential for a subtle, but serious, bug. The problem arises when the same instance of XMLHttpRequest is simultaneously used for different calls. If Call 2 is issued while the object is still waiting for the response of Call 1, what will the callback function receive? In fact, it's even possible the callback function itself is changed before the call comes back. There are ways to deal with this problem, and they're the topic of the Call Tracking pattern. floral crib

Detecting Errors

Sometimes, a request doesn't come back as you expected it, maybe not at all. You scripted the call wrong, or there's a bug in the server, or some part of the infrastructure didn't function properly. Thinking asynchronously is the first step to dealing with these problems, because at least your application isn't blocked. But you need to do more than that.

To detect a server error, you can check the response status using XMLHttpRequest's status flag. This is just a standard HTTP code. For example, if the resource is missing, XMLHttpRequest.status will take on the famous "404" value. In most cases, you can assume anything other than 200 is an error situation. This suggests adding a new check to the callback function of the previous section.

 xhReq.onreadystatechange = function() {
   if (xhReq.readyState != 4)  { return; }
   if (xhReq.status != 200)  {
     // Handle error, e.g. Display error message on page
     return;
   }
   var serverResponse = xhReq.responseText;
   ...
 };

That's great if the browser knows a problem occurred, but sometimes the request will be lost forever. Thus, you usually want some kind of timeout mechanism as well. Using Event Scheduling, establish a timer to track the session. If the request takes too long, the timer will kick in and you can then handle the error. XMLHttpRequest has an abort() function which you should also invoke in a timeout situation. Here's a code example:

   var xhReq = createXMLHttpRequest();
   xhReq.open("get", "infiniteLoop.phtml", true); // Server stuck in a loop.
   var requestTimer = setTimeout(function() {
     xhReq.abort();
     // Handle timeout situation, e.g. Retry or inform user.
   }, MAXIMUM_WAITING_TIME);
   xhReq.onreadystatechange = function() {
     if (xhReq.readyState != 4)  { return; }
     clearTimeout(requestTimer);
     if (xhReq.status != 200)  {
       // Handle error, e.g. Display error message on page
       return;
     }
     var serverResponse = xhReq.responseText;
     ...
   };

Compared to the previous example, a timer has been introduced. The onreadystatechange() callback function will clear the timer once it receives the full response (even if that response happens to be erroneous). In the absence of this clearance, the timer will fire, and in this case, the setTimeout sequence stipulates that abort() will be called and some recovery action can then take place.





Handling POSTs and Other Request Types

So far, we've only looked at a simple GET request - pass in a URL and get a response. As dicussed in the RESTful_Service pattern, it's important to work with other request types as well. "POST", for example, is suited to calls which affect server state or upload substantial quantities of data. To illustrate, let's now create a new service, sumPostGeneric.phtml, that does the same thing as postGet.phtml but with a POST message. It's called "generic" because it reads the full message body text, as opposed to a CGI-style form submission. In this case, it expects a body like "Calculate this sum: 5+6", and returns the sum value.

 <?php

   $body = readBody();
   ereg("Calculate this sum: ([0-9]+)\+([0-9]+)", $body, $groups);
   echo $groups[1] + $groups[2];

   // A PHP method to read arbitrary POST body content.
   function readBody() {
     $body="";
     $putData = fopen("php://input", "r");
     while ($block = fread($putData, 1024)) {
       $body = $body.$block;
     }
     fclose($putData);
     return $body;
   }

 ?>

To POST an arbitrary body, we give XMLHttpRequest a request type of "POST" and pass the body in as an argument to send(). Note that with "GET" queries, the send() argument is null as there's no body content.

   var xhreq = createxmlhttprequest();
   ** xhreq.open("post", "sumPostGeneric.phtml", true); **
   xhreq.onreadystatechange = function() {
     if (xhreq.readystate != 4) { return; }
     var serverresponse = xhreq.responsetext;
     ...
   };
   ** xhreq.send("calculate this sum: 5+6"); **

Quite often, though, you'll be posting key-value pairs, so you want the message to look as if it were submitted from a POST-based form. You'd do that because it's more standard, and server-side libraries make it easy to write web services that accept standard form data. The service below, sumPostForm.php shows how PHP makes light work of such submissions, and the same is true for most languages.

  <?php
    echo $_POST["figure1"] + $_POST["figure2"];
  ?>

For the browser script to make a CGI-style upload, two additional steps are required. First, declare the style in a "Content-Type" header; as the example below shows, XMLHttpRequest lets you directly set request headers. The Second step is to make the body a set of name-value pairs.

   var xhreq = createxmlhttprequest();
   xhreq.open("post", "sumPostForm.phtml", true);
   ** xhReq.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); **
   xhreq.onreadystatechange = function() {
     if (xhreq.readystate != 4) { return; }
     var serverresponse = xhreq.responsetext;
     ...
   };
   ** xhreq.send("figure1=5&figure2=6"); **

"GET" and "POST" are virtually ubiquitous, but RESTful Service points out there's a time and place for other request methods too, such as "PUT" and "DELETE". You don't have to do anything special with those other methods; just set the request type in the open() call, and send() an appropriate body (the item you're putting in the case of "PUT"; a null argument in the case of "DELETE").

Accessible URLs

On learning about XMLHttpRequest, a common reaction is to start dreaming up an interface that pulls in content from popular websites and mashes it altogether to into one big Web 2.0 souffle. Unfortunately, it's not so simple because of a key security rule imposed by all major browsers: XMLHttpRequest can only access content from the originating server. If your application lives at http://ajax.shop/admin, then your XMLHttpRequest objects can happily read http://ajax.shop/admin/products.html and http://ajax.shop/products/contents.html, might be able to read http://books.ajax.shop/contents.html (there's not much documentation on subdomain access, making this a portability concern at best), and definitely won't have access to http://google.com.

The constraint will be familiar to developers of Java applets and Flash, where the same kind of restriction has always been in place. It's there to prevent several kinds of abuse, such as a malicious script uploading confidential user data to an external server. Some have questioned whether it's overkill, but the rule's likely to be sticking around.

So how, then, do all those Ajax mashup sites work? The answer is that all cross-domain transfers run through the originating server, which acts as a kind of proxy - or tunnel - allowing XMLHttpRequests to communicate with external domains. Cross-Domain Proxy elaborates on the pattern.

  • correction: Cross-domain xhr is possible with changes to the client. In IE6 (but not IE7) adding the domain as a trusted site will permit cross-domain requests to the trusted sites. In Firefox/Mozilla/Gecko changing signed.applets.codebase_principal_support to true (in about:config) as well as using "netscape.security.PrivilegeManager.enablePrivilege('UniversalBrowserRead');" when the request is created will allow cross-domain requests. In most cases a confirmation warns the client about the cross-domain access when the request is attempted.

XML Responses

So far, the discussion has swiftly ignored the big elephant in the room: XML. XMLHttpRequest, as its name suggests, was originally designed with XML in mind. As we've seen here, it will actually accept any kind of response, so what's special about XML? So far, responses have been accessed via the responseText field, but XMLHttpRequest also offers an alternative accessor, responseXML. If the response is XML, responseXML represents the result of parsing it and storing it as a DOM object.

The DOM Manipulation patterns have already illustrated how Javascript supports manipulation of DOM objects. In those patterns, we were only interested in one particular DOM object, that representing the current page. But you can manipulate any other DOM object just as easily. Thus, it's sometimes convenient to have a web service output XML content and manipulate the corresponding DOM object.

The prerequisite here is a web service that outputs valid XML. There are many libraries and frameworks around for automatically generating XML from databases, code objects, files, or elsewhere. But don't think you have to start learning some fancy XML library in order to create XML web services, because it's fairly easy to hand-code them too. The service just needs to output an XML Content-type header followed by the entire XML document. Here's an XML version of the sum service shown earlier - it outputs an XML document containing the input figures as well as the sum result.

  <?php

    header("Content-Type: text/xml");
    $sum = $_GET["figure1"] + $_GET["figure2"];
    echo <<< END_OF_FILE
  <sum>
    <inputs>
      <figure id="1">{$_GET["figure1"]}</figure>
      <figure id="2">{$_GET["figure2"]}</figure>
    </inputs>
    <outputs>$sum</outputs>
  </sum>
  END_OF_FILE

  ?>

The call sequence is the same as before, but the callback function now extracts the result using responseXML. It then has a first-class DOM object and can interrogate it using the standard DOM API.

   var xhReq = createXMLHttpRequest();
   xhReq.open("GET", "sumXML.phtml?figure1=10&figure2=20", true);
   xhReq.onreadystatechange = function() {
     if (xhReq.readyState != 4) { return; }
     ** xml = xhReq.responseXML;
     var figure1 = xml.getElementsByTagName("figure")[0].firstChild.nodeValue;
     var figure2 = xml.getElementsByTagName("figure")[1].firstChild.nodeValue;
     var sum = xml.getElementsByTagName("outputs")[0].firstChild.nodeValue; **
     ...
   };
   xhReq.send(null);
 });

The name "XMLHttpRequest" relates to its two primary functions: handling HTTP requests and converting XML responses. The former function is critical and the latter is best considered a bonus. There are certainly good applications for XML responses - see XML_Message, XML_Data_Island, and Browser-Side_XSLT - but keep in mind that XML is not a requirement of Ajax systems.

You can also upload XML. In this case, XMLHttpRequest doesn't offer any special XML functionality; you just send the XML message as you would any other message, and with an appropriate request type (e.g. "POST" or "PUT"). For the sake of the receiving web service, the JavaScript should generally declare the XML content type in a request header.

 xhReq.setRequestHeader('Content-Type',  "text/xml");

The XMLHttpRequest API: A Summary

We've looked at how to achieve typical tasks with XMLHttpRequest, and now here's a quick summary of its properties and methods. It's based on a well-cited Apple Developer Connection article which outlines an API that's supported by IE5+, the Mozilla family (which should include all versions of Firefox), and Safari 1.2+.

XMLHttpRequest has the following properties:

  • onreadystatechange: The callback function that's notified of state changes. (See "Asynchronous Calls" above.)
  • readyState: State within the request cycle. (See "Asynchronous Calls" above.)
  • responseText: The response from the server, as a String.
  • responseXML: The response from the server, as a Document Object Model, provided that the response was valid XML.
  • status: HTTP response code received from the server, e.g "404".
  • statusText: HTTP response code description received from the server, e.g. "Not Found".

And these are XMLHttpRequest's methods:

  • abort(): Stops the request and resets its readyState back to zero. (See "Detecting Errors" above.)
  • getAllResponseHeaders(): Returns a string of all response headers, separated by a newline as in the original message.
  • getResponseHeader(headerField): Returns the value for a particular header field.
  • open(requestMethod, url, asynchronousFlag, username, password): Prepares XMLHttpRequest. Only the first two parameters are required. username and password can be used for authentication. (See Solution introduction above.)
  • send(bodyContent): Sends the message along with specified body content (null if no body content is to be sent, e.g. for GET requests). (See Solution introduction above.)
  • setRequestHeader(headerField, headerValue): Sets a request header. (See "Handling POSTs and Other Request Types" above).


Reapplying frequently will ensure lips retain a protective layer. And it's worth remembering that sweating occurs in hot weather so homemade lip balm disappears quickly.

Decisions

What kind of content will web services provide?

As mentioned in the solution, XML is not the only kind of content that XMLHttpRequest can deal with. As long as you can parse the message in Javascript, there are various response types possible. The Web Services patterns highlight a number of response types, including HTML, XML, JSON, and plain-text.

How will caching be controlled?

It's possible that an XMLHttpRequest response will be cached by the browser. Sometimes, that's what you want and sometimes it's not, so you need to exert some control over caching.

With cache control, we're talking about "GET" based requests. Use "GET" for read-only queries and other request types for operations that affect server state. If you use "POST" to get information, that information won't be cached. Likewise, if you use "GET" to change state, you run the risk that the call won't always reach the server, because the browser will cache the call locally. There are other reasons to follow this advice too; see RESTful Service.

Often, you want to suppress caching in order to get the latest server information, in which case a few techniques are relevant. Since browsers and servers vary in their behaviour, the standard advice is spread the net as wide as possible, by combining all of these techniques:

  • You can add a header to the request:
 xhReq.setRequestHeader("If-Modified-Since", "Sat, 1 Jan 2005 00:00:00 GMT");
 var url = "sum.phtml?figure1=5&figure2=1&timestamp=" + new Date().getTime();
 header("Expires: Sat, 01 Jan 2005 00:00:00 GMT");
 header("Last-Modified: ".gmdate( "D, d M Y H:i:s")."GMT");
 header("Cache-Control: no-cache, must-revalidate");
 header("Pragma: no-cache");

Caching is sometimes a good thing when the service is time-consuming to call and when it's unlikely to have changed recently. To encourage caching, you can adjust the above advice, i.e. set the "If-Modified-Since" and "Expires" headers to a suitable time in the future. In addition, a good approach for smaller data is to cache it yourself, using a Javascript data structure. Browser-Side Cache explains how.

How will you deal with errors?

The section on error detection left open the question of what to do once we discover a server timeout or non-standard error code. There are three possible reactions:

  • Try again: Retry a few times before giving up.
  • Inform the user: Tell the user what's gone wrong and what the consequences are. For instance, inform them that their data hasn't been submitted and they should try again in a few minutes.
  • Do nothing: Sometimes, you have the luxury of ignoring the response (or lack thereof). That might be because you're issuing a low-importance Fire-And-Forget, where you're uploading some data without waiting for any response.


Real-World Examples

Data Grid, Tab Control, Calender, image editor

Lokesh's controls ([www.acceleration.somee.com/controlexample.html www.acceleration.somee.com/controlexample.html]) provide great flexibility in developing dynamic web applications. Bold textItalic text

Lace Chat

Brett Stimmerman's Lace Chat is an Ajax chat application that uses XMLHttpRequest in two ways: to upload messages you type, and to download all the latest messages from the server.

Ajaxium

AJAXIUM ajax for asp.net - replaces ASP.NET postbacks by AJAX (acts as universal AJAX container for ASP.NET pages and controls). Search engines and old browsers friendly due to automatic degradation to the classic ASP.NET if needed.

Backbase

Backbase offer a Demo RSS Reader, which uses XMLHttpRequest to pull down titles of recent articles. When you click on one of those titles, a new XMLHttpRequest will pull down the entire content.

Anyterm

Phil Endecott's Anyterm is an Ajax terminal emulator allowing you to run telnet or SSH within the browser. It uses XMLHttpRequest Calls to upload keystrokes and download the latest screen state.

Mint

Mint is a website statistics package. Site owners include Mint javascript on each page, which quietly inspects the user's browser settings and uploads them using an XMLHttpRequest.


Code Examples

AjaxCaller

The example referenced in the Solution above covers most typical XMLHttpRequest usage. In practice, many people adopt a wrapper library rather than calling XMLHttpRequest directly. That's the approach taken with all of the Ajax Patterns demos, which use a library called ajaxCaller.js that was developed in parallel to the demos themselves. It's a fairly basic library, but offers a simple interface for the functionality that's typically required of XMLHttpRequest. In this section, I'll introduce the library by showing a few usages within the AjaxCaller Test Demo.

The simplest call is getting some plain text: just specify the URL and the callback function.

 ajaxCaller.getPlainText(url, onResponse);

For all calls, the callback function always takes three arguments. The first argument is the result, either a string or a DOM object. The second is an associative array mapping header fields to header values. The third is a "calling context". You can think of this as an optional value that travels alongside the request and the corresponding response, returned to the callback function in exactly the same form as you passed it in when the call was issued. Usually it holds information about the call, e.g. if the call was made to send off a purchase order, the calling context might contain the item that was ordered. Then, ajaxCaller will pass the context into the callback function, which can mark the item as successfully ordered. In reality, the calling context is not actually passed to and from the server; ajaxCaller actually keeps it locally and tracks each pending request. If this all sounds a bit complicated, check out Call Tracking.

The callback function, then, looks like this (assuming ajaxCaller was given the name, "onResponse"):

 function onResponse(text, headers, callingContext) {
   // Use text (a string), headers, and callingContext
 }

And since it's only the text that's used most of the time, the function can be declared in a simpler form:

 function onResponse(text) {
   // Use text (a String)
 }

getPlainText() is on of four commonly-used methods. The others are getXML(), postForPlainText(), postForXML(). Together, these four cover both common request types ("GET" and "POST") and both response types (text and XML).

 ajaxCaller.getXML(url, callbackFunction);
 ajaxCaller.getPlainText(url, callbackFunction, callbackContext);
 ajaxCaller.postForPlainText(url, callbackFunction, callbackContext);

There are also a number of more general methods, for example get() provides a more flexible "GET" requests. In addition to a URL and a callback function, get() lets you specify some variables to be appended to the URL, a flag to indicate whether the response is XML, and the callingContext as discussed above.

 var vars = {
   flavour: "chocolate",
   topping: "nuts"
 };
 ajaxCaller.get("httpLogger.php", vars, onResponse, false, "iceCreamRequest");

There are general operations for other request types too. postVars() creates a CGI-style POST upload and postBody() creates an arbitrary-body POST upload. There are similar methods for other request types, e.g. PUT, TRACE, OPTIONS, DELETE, HEAD.


Alternatives

This section lists all alternatives I'm aware of. Some are more limited than others, and some of the more obscure techniques are included for the sake of completeness, and also in the hope they might spark a few ideas.

IFrame Call

IFrame Call is the main alternative to XMLHttpRequest. Like XMLHttpRequest, it allows for remote calls using GET, POST, and other request types. Here's a summary of XMLHttpRequest Call benefits over IFrame Calls:

  • XMLHttpRequest is cleaner, because it's designed specifically for web remoting. The API is easier to use, especially when it comes to non-GET request types (e.g. POST).
  • XMLHttpRequest offers functionality not available to IFrame, such as the ability to abort a call and track the call's state. This can have important performance implications.
  • XMLHttpRequest is typically faster, especially with shorter responses.
  • XMLHttpRequest parses XML in a simple, portable, manner; IFrame is unrelated to XML.
  • On those browsers that do support XMLHttpRequest, the API is more consistent than that of IFrame.
  • XMLHttpRequest is rapidly gaining the virtue of widespread familiarity.

For all these reasons, XMLHttpRequest should be the default choice. However, there are some specialised situations where IFrame Calls are superior. To wit:

  • IFrame works on older browsers, many of which don't support XMLHttpRequest.
  • IFrame has some benefits for browser history and bookmarkability, at least for IE, as discussed in Unique URLs.
  • XMLHttpRequest on IE won't work if security measures disable ActiveX, a policy sometimes enforced in the enterprise.
  • IFrame respects appropriate changes to document.domain, XMLHttpRequest does not. (In other words, if you have content on different named servers within your domain -- www.example.com, cgi.example.com, archive.example.com, etc.example.com -- XMLHttpRequest will only work for one of those hosts. IFrame can be made to work by putting <script type="text/javascript">document.domain='example.com'</script> into the response body.) THAT SAID: you can use an iframe to host an XMLHttpRequest context.
  • IFrame allows for file uploads and downloads, which are not submittable via javascript.

HTTP Streaming

HTTP Streaming also allows for web remoting, and unlike XMLHttpRequest, the connection remains open. Functionally, the key advantage over XMLHttpRequest is that the server can continuously push new information to the browser. From a resource perspective, streaming is good insofar as there are fewer connections, but has major scaleability problems as it's not feasible to keep open huge amounts of connections and server-side scripts.

Richer Plugin

The Richer Plugin pattern discusses Java, Flash, and other plugins and extensions. These components often have permission to call the server programmatically, and in some cases, can be used as proxies available Javascript code.

On-Demand Javascript

On-Demand Javascript describes a couple of ways to download Javascript on the fly. One involves XMLHttpRequests, but the other is an alternative transport mechanism. It works by adding a script element to the document body, which has the effect of automatically pulling down a named Javascript file. Unlike XMLHttpRequests (or IFrames), browsers impose no domain restrictions on <script> elements. Unlike XMLHttpRequests, there seems to be no good way to detect HTTP errors.

Import XML Document

There's a technique specifically for retrieving XML documents which uses similar technology to XMLHttpRequest. Peter-Paul Koch described the technique in 2000, and recently suggested it can now be written off.

Image-Cookie Call

Brent Ashley's RSLite library is an unusual alternative based on images and cookies. An image's source property is set to the service URL, which is simply a means of invoking the service. The service writes its response into one or more cookies, which will then be accessible from the Javascript once the call has completed.

Note also that an image has three relevant event handlers (onload, onerror, onabort). The onload/onerror event handlers can, between them, represent a boolean result -- and sometimes this is adequate (for example: when all you need is confirmation that your information has been saved). While XMLHttpRequest also supports this, many alternatives (especially those which support cross-domain requests) do not. Browsers impose no domain restrictions on images.

Stylesheet Call

Another way to get at server state is to dynamically change a CSS stylesheet. Just like setting a new Javascript or image source, you set a stylesheet's href property to point to the web service. In Julien Lamarre's demo of this technique, the web service actually outputs a stylesheet, and the response is embedded in the URL of a background-image property!

204 Response

When the server outputs a 204 "No Content" code, the browser won't actually refresh. Using Javascript, you can dynamically submit a form to that such a service if you want to perform a "fire-and-forget" call.

Flash-enabled XHR

Flash-enabled_XHR is a way of having an identical API clone of native XHR, but with cross-domain capabilities.


Related Patterns

Visual Metaphor

An XMLHttpRequest Call is like a quick side conversation - it doesn't have to interrupt the main flow of user-browser activity.


Want to Know More?