Wednesday, April 18th, 2007

Make the XHR abstraction shorter

Category: Ajax, Tip

In low-level land, the try/catch trick that Prototype uses was king for ‘most fun way to get the XHR object’.

Nicolas Faugout has put his hat in the ring with:

javascript

  1. if (!XMLHttpRequest) {
  2.   function XMLHttpRequest() {
  3.     return new ActiveXObject('Microsoft.XMLHTTP');
  4.   }
  5. }

This feels like the Just Another Perl Hacker hacks in Perl land. Next we will see examples with rot13.

Posted by Dion Almaer at 5:13 am
18 Comments

+++--
3.5 rating from 50 votes

18 Comments »

Comments feed TrackBack URI

This is so neat! Expect jQuery and MochiKit to be pouncing on this and adding it to their library.

Comment by Jordan — April 18, 2007

/me looks at date – nope, the 1st has been well gone.

why is this news? the try/catch thing used by most frameworks is because Microsoft.XMLHTTP is not always available (depends on the version of MSXML, or something).

by removing the other versions, you will be restricting your IE audience.

Comment by Kae Verens — April 18, 2007

Returning from a constructor is not exactly something you’re supposed to do. It might work in IE, but it won’t work in other browsers.

Comment by Mark Wubben — April 18, 2007

There is a reason behind using the try catch other than determining version suport:
http://radio.javaranch.com/pascarello/2006/02/07/1139345471027.html

Comment by Eric Pascarello — April 18, 2007

Some assumptions you might have taken, but are wrong:

On IE6, !XMLHttpRequest doesn’t return false here.
That’s because IE reads the “function”s on the page before running thru the code. It means, it reads the code, finds all the functions and makes the

functions list for the current scope. When it gets to “function XMLHttpRequest” it defines window[“XMLHttpRequest”]=function () { … } (window is the

current scope). Only THEN, after declaring the functions, it goes thru the whole code from line 1.

This is NOT true for Gecko.

function a() {
alert("a");
}
if (false) function a() {
alert("b");
}
a();

In the above code, IE will shout “b”, and FF will shout “a”. That is because what I explained before.
Accessing to a() is available before “function a()” is declared, and IE takes the LAST declaration no matter where it is (assuming it’s in the same scope).
FF declares the if statement as scoped statement.
The following code will explain:

a();

if (true) {
function a() {
alert("a");
}
a();
}
function a() {
alert("b");
}

For FF, the result is “b”, “a”.
For IE6, it’s “b”, “b”.

On IE then, the !XMLHttpRequest returns TRUE because it is declared.
FF ignores the statement because XMLHttpRequest does exist and as mentioned before, doesn’t declares “function XMLHttpRequest” as a functions.

BTW, declaring “f=function ()” is different than “function f()”. The latter is accessible from everywhere in the same scope, unlike the first that accessible only after being declared.

Comment by Elad Ossadon — April 18, 2007

this is what i use
its in the constructor of the core ajax “query” class

//————————————————————–
// constructor
//————————————————————–
this.setGlobals = function(){
if(window.ActiveXObject){isIE = true;}
objREQ = isIE == false ? new XMLHttpRequest() : new ActiveXObject(“Microsoft.XMLHTTP”) ;
my_AJAX = this;
}

Comment by eugene a — April 18, 2007

A little explaination on my code :
– Elad, I don’t see any incompatibility between what you say and my code. Think about the !XMLHttpRequest beeing here for FF not to redefine XMlHttpRequest.
As for IE, if the function is declared during the reading process you described, that’s great, because it is what we want on IE ;)
– Kae, I agree with you, this code need some diggin

What I wanted to introduce, after the if then else checking browser version stuff and the magic try/catch Prototype solution is a new approach that would consist of defining once for all (at run time) a function named XMLHttpRequest in order not to introduce a new name for instanciating AJAX calls (when not using any framework of course).
Feel free to adapt the idea to your needs (several ActiveXObject uses, etc..)

Comment by Nicolas Faugout — April 18, 2007

@Nicolas – That’s bad programming, relying on a logical error, even if it works. Besides, like @Jonathan said, it’ll overwrite IE7’s native object.

If it wasn’t the XMLHttpRequest, or “else” clause would’ve been written, as I mentioned in my code – the two browsers would present different results.

You can put a bit more characters and make it work as it should be and still maintaining the ‘good programming’, by asking for ‘window.XMLHttpRequest’ and assigning to it.


if (!window.XMLHttpRequest) {
window.XMLHttpRequest=function () {
return new ActiveXObject('Microsoft.XMLHTTP');
}
}

Comment by Elad Ossadon — April 18, 2007

I really appreciate staying away from prototype’s ridiculous try/catch technique. It triggers firebug because there is an error for each XHR request. I only visit Ajaxian with IE because of that.

Comment by Kris Zyp — April 18, 2007

@Elad – I owe you one! I didn’t know IE was reading the script AND assigning functions as it discovers them even neasted in the code. Your version of course solves the IE 7 issue.

Comment by Nicolas Faugout — April 18, 2007

@Jonathan, I get this in Firefox:

new function(){return 1} -> [object Object]

5 * (new function(){return 1}) -> NaN

Comment by Mark Wubben — April 18, 2007

@Kris Zyp,

Um… I’ve never had Firefox/Firebug generate an error using Prototype, nor accessing its Ajax object. Not once.

Comment by Trevor — April 18, 2007

@Mark – that’s because new operator returns the object itself, which is not 1 but an instance of the function, regardless the ‘return’ statement.
To achieve that kind of syntax, you should override the valueOf method, like this:

function GetFive() {}
GetFive.prototype.valueOf=function () { return 5; }
alert(5*new GetFive());

(very similar like overriding the toString method).

Comment by Elad Ossadon — April 18, 2007

@Kris – Why don’t you just disable Firebug for Ajaxian? I’m not even sure why you’re using it during regular browsing, in my experience it just slows things down. I keep it disabled and only turn it on when I explicitly need it.

Comment by Matt — April 18, 2007

Elad, yes, that works. It doesn’t change the example posted here though :)

Comment by Mark Wubben — April 19, 2007

Interesting. jQuery does the following since the early days:

if ( !window.XMLHttpRequest )
XMLHttpRequest = function(){
return new ActiveXObject("Microsoft.XMLHTTP");
};

Comment by Klaus Hartl — April 19, 2007

;) Klaus

Comment by Miguel Benevides — April 19, 2007

This still does detection on every request. Why not branch at load?

Comment by Dustin Diaz — April 23, 2007

Leave a comment

You must be logged in to post a comment.