Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Javascript > David Mark's Daily Javascript Tips - Volume #3 - Tip #10 - How toCreate an XHR (Ajax) Object

Reply
Thread Tools

David Mark's Daily Javascript Tips - Volume #3 - Tip #10 - How toCreate an XHR (Ajax) Object

 
 
David Mark
Guest
Posts: n/a
 
      12-07-2011
How to Create an XHR (Ajax) Object


var xhrCreated;

// As always, shorthand feature detection for example--use
isHostMethod in this case

// Degrades in IE 6-

if (this.XMLHttpRequest)

// Use try-catch as this host object may explode on creation

try {
var x = new XMLHttpRequest();
xhrCreated = true;
} catch(e) {
}
}

if (xhrCreated) {
var createXhrObject = function() {
return new XMLHttpRequest();
};
}

if (createXhrObject) {

// Put app that must create XHR objects here

}


Thanks to IE, you can't assume that file protocol will work. Put
something like this at the top of your xhrSend function:-

if (uri.slice(0, 5) == 'file:') {
throw new Error('No file prototcol requests with this rendition!');
}

....and take it out on deployment. Pattern of builder should be clear
at this point: checkboxes for degradation points and other questions
of context, plus a checkbox for "debug version", which includes lots
of additional throws to keep the developers honest. It should be
obvious that the build flavor must be settled on at *design* time, not
at deployment. All you do at deployment is uncheck "debug version"
and get the same exact code you developed with, minus the cattle
prods.

Contrast this with breaking up all of your supporting code into tiny
bits, loading them dynamically with script and dealing with all of the
related complications, then running all of these bits through a
"compiler" to piece together something that might approximate what you
encountered during development. Doesn't matter if you release to QA
testers or directly to an unsuspecting public (the norm), such a
strategy is obviously error-prone and self-defeating (not to mention
silly). Error in line 1!

But I digress. What about IE 7 and under? Same pattern, different
objects (ActiveX to be specific). If you must provide an "Ajax
experience" for those stuck with these older IE versions, put the
ActiveX crap in a script inside conditional comments.

I'm thinking that's another option for the builder (splitting the
build in two, with one file to be included in IE conditional
comments). If that's too hard for the average Web developer to deal
with, it could also spit out an HTML template that includes the two
scripts. They will certainly need that for the next installment,
which is dealing with the onload "problem".

In my experience, such IE fallback scripts are usually aimed at 8 and
under, but this one is only needed for 6 and under, so do this:-

if (!createXhrObject) {

// Create ActiveX rendition here

}

....or break it out into another file, aimed only at IE 7 and under.

If you need to use the "file" protocol, drop the check for
createXhrObject and set that reference to null before starting the
ActiveX tests (In IE, you need the ActiveX version of XHR to do "file"
requests).

It's worth noting that much of the feature detection/testing is
unneeded in these IE fallback renditions if they are to be included
exclusively in conditional comments. I suppose that's another option
for the builder.

As for sending a request and reacting to the asynchronous (and they
must *always* be asynchronous) callbacks, I'll leave that for another
time. Look into the send method of the Requester object (in My
Library) for the basics. Having used that object (one of the small
bits of My Library that I actually still use) on some recent projects,
I noticed a design mistake: there should never have been an "oncancel"
callback and there should have been an "ontimeout" callback (currently
"oncancel" serves both). The cancel operation is programmatic and the
timeout is not. So the former doesn't need a callback as it is rarely
useful to call your code back due to its own actions. In this case,
it can lead to some ugly logic in the "oncancel" callback (to
determine whether the call is a result of a timeout or not).

What can you expect from the typical do-everything library or
framework? Again, God only knows. For the longest time, jQuery
didn't account for the fact that the ActiveX objects could blow up (no
try-catch until about the fifth version). Things like Dojo and YUI
use browser sniffing and such projects are too large to recover from
such mistakes; there's just not enough interested parties to go back
and rewrite all of the botched code. I tried to fix Dojo once, but
the authors were steadfastly opposed to "patches" that involved more
than a few lines of code at a time. I don't know if that was because
of general delusion or just the fear of appearing incompetent, but at
that rate, it would have taken thirty years to shake all of the bugs
out. So their developers' egos were saved, but when IE 9 hit, every
Dojo application ever written (all ten) fell apart.

Interestingly enough, Dojo's XHR "module" actually requires their
botched query module. That's the "best minds in the industry for
you": not thinking beyond today at all; and not thinking much today
either. How many versions of Dojo and YUI actually turned out to be
reusable (beyond the brand name)? Redo-able is what they meant.

http://www.cinsoft.net/
http://twitter.com/cinsoft
http://jsperf.com/browse/david-mark
 
Reply With Quote
 
 
 
 
Andreas Bergmaier
Guest
Posts: n/a
 
      12-08-2011
David Mark schrieb:
> How to Create an XHR (Ajax) Object
>
>
> var xhrCreated;
>
> // As always, shorthand feature detection for example--use
> isHostMethod in this case
>
> // Degrades in IE 6-
>
> if (this.XMLHttpRequest)
>
> // Use try-catch as this host object may explode on creation
>
> try {
> var x = new XMLHttpRequest();
> xhrCreated = true;
> } catch(e) {
> }
> }
>
> if (xhrCreated) {
> var createXhrObject = function() {
> return new XMLHttpRequest();
> };
> }
>
> if (createXhrObject) {
>
> // Put app that must create XHR objects here
>
> }
>
> In my experience, such IE fallback scripts are usually aimed at 8 and
> under, but this one is only needed for 6 and under, so do this:-
>
> if (!createXhrObject) {
>
> // Create ActiveX rendition here
>
> }


What do you think about

if (!window.XMLHttpRequest)
window.XMLHttpRequest = function() {
try {
return new (window.XMLHttpRequest = function XMLHttpRequest() {
ActiveXObject.call(this, "Microsoft.XMLHTTP");
});
} catch(e) {
try {
return new (window.XMLHttpRequest = function XMLHttpRequest() {
ActiveXObject.call(this, "Msxml2.XMLHTTP");
});
} catch(e) {
delete window.XMLHttpRequest;
throw new Error("This browser doesn't support any AJAX");
}
}
};

And then just use new XMLHttpRequest() whereever you need it?

Bergi
 
Reply With Quote
 
 
 
 
David Mark
Guest
Posts: n/a
 
      12-08-2011
On Dec 7, 11:41*pm, Andreas Bergmaier <(E-Mail Removed)> wrote:
> David Mark schrieb:
>
>
>
>
>
>
>
>
>
> > How to Create an XHR (Ajax) Object

>
> > var xhrCreated;

>
> > // As always, shorthand feature detection for example--use
> > isHostMethod in this case

>
> > // Degrades in IE 6-

>
> > if (this.XMLHttpRequest)

>
> > * *// Use try-catch as this host object may explode on creation

>
> > * *try {
> > * * *var x = new XMLHttpRequest();
> > * * *xhrCreated = true;
> > * *} catch(e) {
> > * *}
> > }

>
> > if (xhrCreated) {
> > * *var createXhrObject = function() {
> > * * *return new XMLHttpRequest();
> > * *};
> > }

>
> > if (createXhrObject) {

>
> > * *// Put app that must create XHR objects here

>
> > }

>
> > In my experience, such IE fallback scripts are usually aimed at 8 and
> > under, but this one is only needed for 6 and under, so do this:-

>
> > if (!createXhrObject) {

>
> > * *// Create ActiveX rendition here

>
> > }

>
> What do you think about
>
> if (!window.XMLHttpRequest)
> * * window.XMLHttpRequest = function() {


Don't augment host objects and don't obscure browser differences.

> * * * *try {
> * * * * * return new (window.XMLHttpRequest = function XMLHttpRequest() {
> * * * * * * *ActiveXObject.call(this, "Microsoft.XMLHTTP");
> * * * * * });


Why are you setting the property again?

> * * * *} catch(e) {
> * * * * * try {
> * * * * * * *return new (window.XMLHttpRequest = functionXMLHttpRequest() {
> * * * * * * * * ActiveXObject.call(this, "Msxml2.XMLHTTP");
> * * * * * * *});
> * * * * * } catch(e) {
> * * * * * * *delete window.XMLHttpRequest;


Never attempt to delete properties of a host object

> * * * * * * *throw new Error("This browser doesn't support any AJAX");


Why do that?

> * * * * * }
> * * * *}
> * * };
>
> And then just use new XMLHttpRequest() whereever you need it?


You don't want to spoof that method as other scripts may get
confused. The ActiveX implementations are not exactly the same, so
create your own wrapper.
 
Reply With Quote
 
Thomas 'PointedEars' Lahn
Guest
Posts: n/a
 
      12-08-2011
Andreas Bergmaier wrote:

> David Mark schrieb:
>> How to Create an XHR (Ajax) Object
>>
>> var xhrCreated;
>>
>> // As always, shorthand feature detection for example--use
>> isHostMethod in this case
>>
>> // Degrades in IE 6-
>>
>> if (this.XMLHttpRequest)
>> // Use try-catch as this host object may explode on creation
>>
>> try {
>> var x = new XMLHttpRequest();
>> xhrCreated = true;
>> } catch(e) {
>> }
>> }
>>
>> if (xhrCreated) {
>> var createXhrObject = function() {
>> return new XMLHttpRequest();
>> };
>> }
>>
>> if (createXhrObject) {
>> // Put app that must create XHR objects here
>> }
>>
>> In my experience, such IE fallback scripts are usually aimed at 8 and
>> under, but this one is only needed for 6 and under, so do this:-
>>
>> if (!createXhrObject) {
>> // Create ActiveX rendition here
>> }

>
> What do you think about
>
> if (!window.XMLHttpRequest)
> window.XMLHttpRequest = function() {
> try {
> return new (window.XMLHttpRequest = function XMLHttpRequest() {
> ActiveXObject.call(this, "Microsoft.XMLHTTP");
> });
> } catch(e) {
> try {
> return new (window.XMLHttpRequest = function XMLHttpRequest()
> {
> ActiveXObject.call(this, "Msxml2.XMLHTTP");
> });
> } catch(e) {
> delete window.XMLHttpRequest;
> throw new Error("This browser doesn't support any AJAX");
> }
> }
> };
>
> And then just use new XMLHttpRequest() whereever you need it?


I think it is utter nonsense. I wonder, have you even tried it?

- You are testing a property of the object referred to by `window' when you
are interested in a property of the global object.

- You are attempting to augment the host object referred to by `window'.

- Calling a callable object as property of another object is _not_
equivalent to using it as a constructor.

- You are calling `call' on a host object, but it does not need to inherit
from Function.prototype or otherwise implement that method. Since
`ActiveXObject' implements the method, but does not allow it to be called
on that object ("Automation server cannot create object"), assuming that
no other host-object specific peculiarities are involved, the first
attempt to use `new window.XMLHttpRequest' will delete
`window.XMLHttpRequest' and throw an Error exception; if that exception
is handled, subsequent attempts will throw a built-in TypeError exception.

- The `call' call is pointless. Your constructor does _not_ *return* a
reference to another object (as a factory would), so it returns a
reference to an "empty" Object instance:

return new (window.XMLHttpRequest = function XMLHttpRequest() {
ActiveXObject.call(this, "Microsoft.XMLHTTP");
});

evaluates to

window.XMLHttpRequest = function XMLHttpRequest() {
ActiveXObject.call(this, "Microsoft.XMLHTTP");
};

return new window.XMLHttpRequest();

which, assuming for a moment `call' works here, can be broken down into

var previousXHR = window.XMLHttpRequest;

window.XMLHttpRequest = function XMLHttpRequest() {
ActiveXObject.call(previousXHR, "Microsoft.XMLHTTP");
};

return new window.XMLHttpRequest();

which evaluates to the equivalent of

return {};

As a result, attempts to call XHR methods on the newly created object will
throw a built-in TypeError exception, while attempts to set event
listeners or other non callable properties will probably work but will do
nothing useful.

In case you have problems seeing any of the above, replace the relevant
parts with dummy parts that work basically the same:

var w = {},
global = this;

function A ()
{
console.log("A: ", arguments);

/* Disable this to see what happened if call() would work */
if (this != global)
{
throw new Error("Automation server cannot create object");
}
}

A.prototype.open = function () {};

if (!w.X)
w.X = function () {
try
{
return new (w.X = function X () {
A.call(this, "Microsoft.XMLHTTP");
});
}
catch (e) {
try
{
return new (w.X = function X () {
A.call(this, "Msxml2.XMLHTTP");
});
}
catch (e)
{
delete w.X;
throw new Error("This browser doesn't support any AJAX");
}
}
};

/* Disable this to see what happened if exception was not handled */
try
{
new w.X();
}
catch (e)
{
}

var x = new w.X();

/* TypeError */
x.open();

- You are using a named function expression. Doing that, you are
effectively declaring a function with that name in the local execution
context, due to a JScript bug (which is the ECMAScript implementation of
about the only environment that this code would be relevant for). This is
even more foolish as you are not referring to the function name in the
function (recursion without arguments.callee).

- The `XMLHttpRequest' behavior in JScript/MSHTML varies from that in MSXML,
and might even vary more from it in the future (when XMLHttpRequest2 might
be implemented). So you need to encapsulate this. (ISTM David's original
approach already falls short of that distinction.)

So you *really* do *not* want to do this.


PointedEars
--
realism: HTML 4.01 Strict
evangelism: XHTML 1.0 Strict
madness: XHTML 1.1 as application/xhtml+xml
-- Bjoern Hoehrmann
 
Reply With Quote
 
Thomas 'PointedEars' Lahn
Guest
Posts: n/a
 
      12-08-2011
Thomas 'PointedEars' Lahn wrote:

> - The `call' call is pointless. Your constructor does _not_ *return* a
> reference to another object (as a factory would), so it returns a
> reference to an "empty" Object instance:
>
> return new (window.XMLHttpRequest = function XMLHttpRequest() {
> ActiveXObject.call(this, "Microsoft.XMLHTTP");
> });
>
> evaluates to
>
> window.XMLHttpRequest = function XMLHttpRequest() {
> ActiveXObject.call(this, "Microsoft.XMLHTTP");
> };
>
> return new window.XMLHttpRequest();


That is not entirely accurate (`x = y` evaluates to the value of _`y'_ if
not in JavaScript 1.2 compliance mode), but you get the idea.

> which, assuming for a moment `call' works here, can be broken down into
>
> var previousXHR = window.XMLHttpRequest;
>
> window.XMLHttpRequest = function XMLHttpRequest() {
> ActiveXObject.call(previousXHR, "Microsoft.XMLHTTP");
> };
>
> return new window.XMLHttpRequest();
>
> which evaluates to the equivalent of
>
> return {};


PointedEars
--
Prototype.js was written by people who don't know javascript for people
who don't know javascript. People who don't know javascript are not
the best source of advice on designing systems that use javascript.
-- Richard Cornford, cljs, <f806at$ail$1$(E-Mail Removed)>
 
Reply With Quote
 
David Mark
Guest
Posts: n/a
 
      12-09-2011
On Dec 8, 5:12*pm, Thomas 'PointedEars' Lahn <(E-Mail Removed)>
wrote:

> - The `XMLHttpRequest' behavior in JScript/MSHTML varies from that in MSXML,
> * and might even vary more from it in the future (when XMLHttpRequest2 might
> * be implemented). *So you need to encapsulate this. *(ISTM David'soriginal
> * approach already falls short of that distinction.)
>


No overrideMimeType? That should certainly be part of the
documentation. And it's a hack method anyway.

 
Reply With Quote
 
Thomas 'PointedEars' Lahn
Guest
Posts: n/a
 
      12-09-2011
David Mark wrote:

> Thomas 'PointedEars' Lahn wrote:
>> - The `XMLHttpRequest' behavior in JScript/MSHTML varies from that in
>> MSXML, and might even vary more from it in the future (when
>> XMLHttpRequest2 might be implemented). So you need to encapsulate
>> this. (ISTM David's original approach already falls short of that
>> distinction.)

>
> No overrideMimeType? That should certainly be part of the
> documentation. And it's a hack method anyway.


`file://', for a start.


PointedEars
--
Prototype.js was written by people who don't know javascript for people
who don't know javascript. People who don't know javascript are not
the best source of advice on designing systems that use javascript.
-- Richard Cornford, cljs, <f806at$ail$1$(E-Mail Removed)>
 
Reply With Quote
 
David Mark
Guest
Posts: n/a
 
      12-09-2011
On Dec 9, 5:50*am, Thomas 'PointedEars' Lahn <(E-Mail Removed)>
wrote:
> David Mark wrote:
> > Thomas 'PointedEars' Lahn wrote:
> >> - The `XMLHttpRequest' behavior in JScript/MSHTML varies from that in
> >> * MSXML, and might even vary more from it in the future (when
> >> * XMLHttpRequest2 might be implemented). *So you need to encapsulate
> >> * this. (ISTM David's original approach already falls short of that
> >> * distinction.)

>
> > No overrideMimeType? *That should certainly be part of the
> > documentation. *And it's a hack method anyway.

>
> `file://', for a start.


I already covered that one.
 
Reply With Quote
 
Thomas 'PointedEars' Lahn
Guest
Posts: n/a
 
      12-10-2011
David Mark wrote:

> Thomas 'PointedEars' Lahn wrote:
>> David Mark wrote:
>> > Thomas 'PointedEars' Lahn wrote:
>> >> - The `XMLHttpRequest' behavior in JScript/MSHTML varies from that in
>> >> MSXML, and might even vary more from it in the future (when
>> >> XMLHttpRequest2 might be implemented). So you need to encapsulate
>> >> this. (ISTM David's original approach already falls short of that
>> >> distinction.)

>>
>> > No overrideMimeType? That should certainly be part of the
>> > documentation. And it's a hack method anyway.

>>
>> `file://', for a start.

>
> I already covered that one.


No, a code branch that uses ActiveXObject(…) instead of XMLHttpRequest() if
available or if the URI is a `file:' URI is missing from your suggestion.
It is insufficient, and inappropriate, for a library to throw an exception
when it could work if done differently.

In JSX:http.js, I am creating XHR objects not before they are actually used,
and reuse it if possible. This would allow me to use the type of object
that fits the request URI best (the current HEAD revision has other flaws,
though). However, for the time being I have opted not to use
`XMLHttpRequest' when `ActiveXObject' is available and its use is
successful.

<http://pointedears.de/websvn/filedetails.php?repname=JSX&path=%2Ftrunk%2Fhttp.j s>


PointedEars
--
When all you know is jQuery, every problem looks $(olvable).
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
David Mark's Essential Javascript Tips - Volume #8 - Tip #47E -Attaching and Detaching Event Listeners David Mark Javascript 1 12-17-2011 08:07 AM
David Mark's Daily Javascript Tips - Volume #3 - Tip #8 - How toCompute Styles David Mark Javascript 1 12-07-2011 02:34 AM
David Mark's Javascript Tip Du Jour - Volume #1 - Tip #1234 - How toMeasure Element Dimensions David Mark Javascript 58 12-06-2011 10:13 PM
David Mark's Javascript Daily - Volume #3 - Tip #6 - How to Get andSet HTML David Mark Javascript 6 11-16-2011 06:51 PM
David Mark's Javascript Tip of the Day - Volume #1 - #Tip 14-C David Mark Javascript 16 11-11-2011 02:45 AM



Advertisments