Programming - - By James Edwards

The Best Way to Instantiate Ajax?

Ajax has been around for a while now, and its development practices are quite mature. Like most programmers, I’d settled on an instantiation pattern that looked basically like this:

function AjaxRequest()
{
   var request = null;
   
   if(typeof window.XMLHttpRequest != "undefined")
   {
      request = new XMLHttpRequest();
   }
   else if(typeof window.ActiveXObject != "undefined")
   {
      try
      {
         request = new ActiveXObject("Microsoft.XMLHTTP");
      }
      catch(err) { request = null; }
   }
   
   return request;
}

The code first tests for a native object, which most modern browsers use, then for an ActiveX implementation to support IE6. You’ll find code like this all over the internet.

(Some people test for multiple versions of XMLHTTP, to try to select the most-recent. In my view, it’s completely unnecessary to instantiate advanced versions of MSXML unless you specifically need their advanced features, but that’s beside the point for this post.)

Not so long ago, I was writing a development and testing tool called CSSUtilities — a JavaScript API which provides methods for querying CSS style sheets. I needed Ajax capability to load style sheets; but more specifically, it had to be able to work with local files as well as network files, something which modern browsers do support (though with variation — such requests generally return an HTTP status code of 0, and don’t provide any HTTP headers, which makes sense since they’re not really HTTP requests).

But it was while developing this capability that I discovered something unfortunate — the native XMLHttpRequest object available in IE8 does not work at all for local files. And just to make life harder, the object does successfully instantiate, but attempting to use it triggers a security error — "Access is denied".

This was particularly annoying, because it meant that I couldn’t use it for local files (and therefore, my tool would not work for people developing sites as local pages, which many do even though it’s not a very good way of working). But more frustrating still, it meant that the instantiation pattern had no opportunity to fallback to an ActiveX implementation, because the native object did instantiate, it just didn’t work.

My solution? Simple — instantiate ActiveX first. So if a version of IE is being used that supports both that and a native version, the ActiveX version is preferred; if local files are in use, the ActiveX version will work (if security settings permit it, which they do by default); if remote files are in use and native XHR is disabled, the ActiveX version will still work, or if ActiveX is disabled and native XHR is enabled, the native version will work. Ka’Plah!

function AjaxRequest()
{
   var request = null;
   
   if(typeof window.ActiveXObject != "undefined")
   {
      try
      {
         request = new ActiveXObject("Microsoft.XMLHTTP");
      }
      catch(err) { request = null; }
   }
   if(request === null && typeof window.XMLHttpRequest != "undefined")
   {
      try
      {
         request = new XMLHttpRequest();
      }
      catch(err) { request = null; }
   }
   
   return request;
}

Of course, if ActiveX is disabled and local files are in use, it won’t be possible to instantiate a request; but that was already the case, so we we’ve lost nothing.

The exception-handling around the first attempt is important — so failure can be logged and the reference nullified, then the next condition has an opportunity to run (specified as a second if, rather than else if, which wouldn’t get to run under those circumstances). The exception-handling around the second attempt is just-in-case, so there’s no chance of internal errors getting back to the user.

The point of all this is absolutely not to try and circumvent security or user control — users still have complete control over both native and ActiveX implementations (via Advanced Internet Options, and Security settings, respectively). The point is to give the user (who may also be a novice developer) the opportunity to allow code that they want to work, to work, when their browser options do not give them full control.

Ultimately, this pattern extends user control, and makes it easier for all concerned to give their work a streak-free shine!

So what do you think — have you reached a similar conclusion already, or can you envisage a situation where this pattern might create problems? Perhaps you’ve adopted your own patterns of use that are different from anything discussed here?

Thumbnail credit: nickwheeleroz

Sponsors