DHTML RPC Revisited: XMLHTTP

Summary

The XMLHTTPRequest object was first implemented as a proprietary Active-X control in _WinIE5_. Later it was _emulated_ by Mozilla in an effort to increase that browser's compatibility with DHTML which used the extension. Once that occured, it quickly became a practical necessity for other browsers to implement it in the same way to stay relevant for advanced DHTML.

The current list of browsers which have a useable level of XMLHTTP support is: _WinIE5+_, _Gecko-based?+_ (Mozilla?+, Firefox?+), Konqueor?+, Safari?+, Opera?+, and OmniWeb?+. This wide support has encouraged major public facing applications such as _GMail_ and _Base Camp_ to us to adopt the technology.

Implementation

The web is littered with good, detailed descriptions of how to implement XMLHTTPRequest. For the sake of discussion, lets just use the really simple function below.

/**
 * A super simple XMLHTTPRequest wrapper for handling the most common 
 * cases. Mostly stolen from http://jibbering.com/2002/4/httprequest.html
 *
 * @param url       The URL to request with any querystring already 
 *                  appended.
 * @param callback  A function to call when load is complete. Function will
 *                  be passed the native XMLHTTPRequest object as an 
 *                  argument, which it can examine for results.
 * @param postdata  form-urlencoded data to be sent through POST, or null 
 *                  if none required.
 */
function doXmlHttpRequest(url, callback, postdata) {
  var xmlhttp = null;
  var loaded = false;

  // special conditional compilation comments hide this code from all 
  // browsers except win/ie. 
  
  /*@cc_on @*/
  /*@if (@_jscript_version >= 5)
    try {
      xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
    } catch (e1) {
      try {
        xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
      } catch (e2) {
        xmlhttp = false;
      }
    }
  @end @*/

  if (!xmlhttp && typeof XMLHttpRequest != 'undefined') {
    xmlhttp = new XMLHttpRequest();
  }

  xmlhttp.open(postdata == null ? "GET" : "POST", url, true);
  xmlhttp.onreadystatechange = function() {
    // must check whether previously loaded because of bug in Safari/Opera 
    // where onreadystatechange gets called multiple times with 
    //readyState == 4.
    if (!loaded && xmlhttp.readyState == 4) {
       callback(xmlhttp);
       loaded = true;
    }
  }
  xmlhttp.send(postdata);
}

Response Formats

Once you've decided to use XmlHttp, you need to decide what the response format will be. This decision has a significant impact on the speed, flexibility, and extensibility of your app.

Return XML

If the server returns a valid XML document, you can retreive it through the _responseXML_ property. Note that in order for this property to be non-null in all browsers, the response must be sent with it's Content-type header set to "text/xml". You can then navigate it's DOM tree to extract the returned data.

Assuming the server-side PHP script xmlhttp_server_xml.php contained this code:

<result>
  <serverTime><?= date('r') ?></serverTime>
</result>

... then you could use the following JavaScript to call it and parse the result:

function testXML() {
  doXmlHttpRequest(
  "xmlhttp_server_xml.php", 
  function(xmlhttp) {
    alert("server time is: " + 
          xmlhttp.responseXML.documentElement
          .getElementsByTagName("serverTime")[0].firstChild.nodeValue);
  });
}

The upside of using XML is that it creates a clean, platform-independent, widely-supported server interface. If you intend for other, non-DHTML clients to call this URL, or if they ever end up doing so, they will almost certainly have well-tested libraries available on their platform for manipulating XML. _Google Maps_ is an example of an application using XMLHTTP with an XML response type for this reason.

The downside of XML is that it isn't an optimal format for DHTML itself. Although the XMLHTTPRequest object is highly standardized across browsers, the XMLDocument object is not. The methods for executing XPath expressions for example, which are the best way to navigate a DOM tree, are incompatible across WinIE and Mozilla.

Another downside of returning XML is that it is a very memory and processor intensive format. The entire returned document must be parsed and loaded up into a DOM tree before it can be used. And even then, you will likely have to make at least one more pass over the data, creating intermediate data structures, before the UI can be updated.

Return HTML

In some cases, all you are trying to do is update a section of the UI. If your app's structure and presentation are relatively separated, it may be easy to simply send replacement HTML.

The advantage is that it's very easy. The innerHTML property can be used (in most cases, not with an XHTML strict doctype) to overwrite the contents of some element in one line. For example, if you had the following PHP file called xmlhttp_server_html.php:

<p><strong>Server time is:</strong> <?= date('r') ?>.</p>

... then you could use the following javascript to insert the result into the document:

function testHTML() {
  doXmlHttpRequest(
    "xmlhttp_server_html.php",
    function(xmlhttp) {
      document.getElementById("html-result").innerHTML 
        = xmlhttp.responseText;
    });
}

The downside of returning HTML is that it ties your server interface more tightly to the UI which displays it. If the UI changes significantly, the server interface may have to change as well.

Return JavaScript

A response format which has recently gained some popularity, with _Google Suggest_ for example, is JavaScript. The advantage being that it is extremely fast since it uses the fastest possible parser in DHTML: the JavaScript parser itself.

The strategy is to return some JavaScript from the server which calls a function waiting on the receiving page...

showTime("");

The JavaScript code is then run by using JavaScript's built-in _eval()_ method:

function testJavaScript() {
  function showTime(stime) {
    alert("Server time is: " + stime);
  }
  
  doXmlHttpRequest(
    "xmlhttp_server_js.php",
    function(xmlhttp) {
      eval(xmlhttp.responseText);
    })
}

The downside again of this approach is even tighter coupling to the client UI than returning HTML. However, that might be an appropriate tradeoff for applications requiring ultimate responsiveness and expecting few requirements for alternative clients.

Return Something Else