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

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • http://www.yacare.fr McBenny

    Important point for people who still instantiate their Ajax. As a jQuery user, I’m wondering if jQuery uses the same order or not.
    Does anybody knows ? Shall I explore jQuery code to check ?

  • John

    I use the YUI connection manager so I don’t have to think about things like this

  • http://www.brothercake.com/ James Edwards

    @McBenny — everybody instantiates Ajax, it’s just that a lot of people have deferred responsibility to 3rd party libraries. I don’t know how jQuery works in this respect, though I could look into it; or anything you find out would be interesting information.

    @John — and does it work for local files in IE8?

  • John

    @James I don’t know. I didn’t even know that was a problem.

  • http://logicearth.wordpress.com logic_earth

    I personally would drop all the “if” statements other then the one checking for “ActiveX” and just “return new …ajax-object-thing…” and not assign it to a local varaible. I see no reason to do that. You can also easily do “return null” in the try/catch as well.

    • http://logicearth.wordpress.com logic_earth

      I haven’t tasted it yet, I assume when JavaScript throws an error it stops everything including “return” and goes to the catch. If not then this won’t work.
      http://www.pastie.org/1204096

  • http://www.brothercake.com/ James Edwards

    @John — it’s unlikely to be an issue in most practical cases. The only case I know of where this problem might arise is if you’re making something that needs to work on local files (like a dev tool). But all the same, if you’re going to adopt a standardized pattern at all, you may as well adopt one that works everywhere.

    @logic_earth — Yes, when JS throws an error it abandons the current processes at all scopes, including bypassing any subsequent return or break statements. In fact you can even use the throw statement to force execution to stop (though offhand I can’t think of a reason for doing so). But I don’t see how it would be an advantage over normal flow control? What do you gain by doing that?

    I also think (though not everyone agrees) that exception handling shouldn’t be used willy-nilly just to save code. You should save it for situations where there’s no other way to detect what you need to know (such as detecting ActiveX support, which can’t be established without attempting to instantiate it)

    • http://logicearth.wordpress.com logic_earth

      “But I don’t see how it would be an advantage over normal flow control? What do you gain by doing that?”
      Nothing, It is just my style of writing code. I prefer to use as little if statements and evaluations as possible. It is much easier for me to read the flow this way. It is also my style to avoid setting local variables unless they are absolutely needed.