This guide is a quick romp through AJAX, stopping at all the little pieces that you might not know about.
Making a Request / Response
It turns out that basically every browser on the planet does XMLHttpRequest the same way, with the exception of the evil Internet Explorer, which uses ActiveXObjects distributed with the operating system, and even then it does so inconsistently. In theory, the new IE7 is supposed to conform to the “right” way, but given there are still so many 5.0, 5.0, and 6.0 IE browsers out there, this has made a rats nest out of what should have been simple code to start with. Here’s the fundamental code that returns a browser-neutral object:
function createRequest() { var request = null; try { request = new XMLHttpRequest(); // Everyone but IE } catch (trymicrosoft) { try { request = new ActiveXObject("Msxml2.XMLHTTP"); } catch (othermicrosoft) { try { request = new ActiveXObject("Microsoft.XMLHTTP"); } catch (failed) { request = null; // Always check for NULL! } } } if ( request == null ) // Might as well check here alert("Error creating request object!"); } else return request; }
An aside: It turns out that Internet Explorer 5.x on the Mac (an old, broken, and discontinued product from Microsoft) doesn’t work — AJAX can’t be done, as there is no ActiveX control, and they don’t do it the “standard” way with the browser.
To use such a function in your pages, you’d do something like this:
function getSomething() { var request = createRequest(); var url = "http://www.yourhost.com/serverside"; request.open("GET", url, true ); // true = asynchronous request.onredystatechange = callback; request.send (null); // or whatever data }
Note: if you use POST, then you also have to set the request header, .setRequestHeader(), usually this will be “Content-Type” with a value of “application/x-www-form-urlencoded” when just sending form data. Otherwise the server has no idea what is being sent in the POST.
The callback function, which can be different for each request, needs to check a ready state and a status — as the call back gets called four times during the process:
function callback(request) { if ( request.readyState == 4 ) { // 4 = response downloaded if ( request.status == 200 || // 200 = success request.status == 304 ) { // 304 = not modified // Do something var response = request.responseText; } } }
Another Note: you can use responseXML instead of responseText for XML! You manipulate it just like the DOM, making use of getElementsByTagName(). This requires a Content-Type of “text/xml” from the server to work.
Libraries, like Prototype and jQuery, abstract all this away so that you simply provide the URL, GET/POST type, Asyc/Sync type, and a call back — with special forms to do common tasks, like filling in a DIV with pulled content with one call.
Keep in mind, if you start adding other third-party Prototype libraries, they may fail if you use the jQuery enhancements. Fret not, because you can have your cake and eat it to. jQuery lets you unhook itself from the standard AJAX shortcut conventions.
It’s tempting to skim through this, assuming that “you get it” — but the devil is in the details.
Here’s where things get extra tricky.
Browsers Cache Dynamic Responses
Internet Explorer and Opera actually cache the response to a given URL request. That means if you do a GET, the first one will work, but subsequent ones will not. The browser will go “oh, I remember sending this before, here’s the response I got.” As such, you either need to use POSTs or attach a dummy variable, with something like new Date().getTime() as part of the parameters to force it to a unique URL each time.
Script elements need an end tag!
It’s often useful to put JavaScript into its own .js file. Note however that the SCRIPT tag, for historical reasons, expects content. It can be empty, but there must be containing something.
Illegal: <SCRIPT type=”text/javascript” src=”yourlib.js” />
Legal: <SCRIPT type=”text/javascript” src=”yourlib.js”></SCRIPT>
Never Use innerHTML
Additionally, you’ll find that a lot of examples use innerHTML to set the property of an element, like DIV. This is wrong. It is not part of the DOM specification, the W3C has deprecated it, and future browsers may not support it — in fact, some browsers already don’t support it now. Use DOM code, it works on any platform. Plus, libraries like Prototype and jQuery have special shortcuts making it possible to access elements by id, element, css type, XPath, and even as a collection. Seriously, the examples in your books are dated and wrong – look for methods like .text() and .html() instead.
* Once again, IE has problems, this time with the with the Node type.
Set Behaviors Elsewhere, If You Can
It’s also tempting to sprinkle code in onClick handlers, but that can make modification difficult for mass changes, not to mention making the HTML uglier. A library called Behavior solves this problem elegantly. You write regular, clean HTML and it will use JavaScript to add behaviors to the tags you specify after the fact. Simply define a set of rules and apply them.
DOMs Reorganizes, They Don’t Copy
There’s also some other DOM magic that isn’t obvious. If you have a DOM tree and you get a reference to an element, and then you do an otherElement.appendChild(firstElement) to some other node element, since a DOM node can only have one parent, it actually gets moved. That is, you don’t have to delete anything.
Stuff About AJAX Libraries You Wanna Know
Drag’n’Drop …uh, no… Sortables
Drag’n’Drop in the browser world of AJAX means dragging DIVs and such to different locations on the screen, of which some of those locations can themselves be containers. If you’re looking to rearrange the elements within a container, that is called Sortables. These are container elements (like OL’s and DIV’s) which contain things (like LI’s, DIV’s, and IMG’s), and maintain the order of them. By far the best sortables example I’ve seen was done by Greg Neustaetter and he explains how he did it.
The HTML ID Does Matter!
Turns out many of the AJAX libraries do trickery based upon the ID of the elements. As you’re aware, every ID on a page must be unique in order to pass valid HTML. When the AJAX libraries go looking for elements, this must be true. Additionally, the IDs often have special meanings. For instance, in order to report sequences, the IDs had to be in a form of string underscore integer. ( e.g., Item_10). You can also use a dash instead. AJAX will let you serialize the numerical parts into a string. So, if your id happens to contain additional dashes, underscores, or forgets the numerics, bad things can happen.
Be Careful With Arrays
There’s a lot of clever overloading going on. Sometimes a parameter is an element, sometimes it’s a class name, and sometime it’s an array. When it comes to sortables (and drag’n’drop), often you need to provide a list of valid containers. This is done by creating an array of strings with the appropriate names and passing that to the AJAX call. As such, it isn’t mandatory to have an array to make things sortable, but only when you’re crossing containers.
Metadata
It is actually possible to pass collections of metadata inside of a class tag! This can be very handy.
<P ID="thing" class="foo bar { xyzzy: 'plugh', abc: 123 }" />
jQuery has a plug-in called metadata (the documentation is in the JavaScript code) that lets you access this.
$("thing").data().xyzzy returns "plugh" $("thing").data().abc returns 123
AJAX Responses
If an AJAX response returns text, you can access it with .responseText. If an AJAX response returns XML, you can access it with .responseXML and read it just like you would the DOM. And, if the AJAX response send straight HTML, you can always inject it directly into an element with Prototype’s Ajax.Updater.
Currently, the hard part of the problem is taking an XML response from the server and transforming that fragment into HTML using XSLT on the client side.
Normally, a full XML document is transformed into HTML and loaded into the DOM, to which AJAX takes over. The problem is, while AJAX allows for modifying the content of an element, the phase of XML to HTML is already past. Just as XMLHttpRequest() has many different historical quirks, XSLT support and implementation is even worse.
Supposedly, however, there is a library called zXml, and it has a transformToText() function which, in theory, provides cross browser support.
XSLT and AJAX
XSLT_Example.zip
This example separates content, structure, and presentation.
The magic of XSLT allows the transformation of any arbitrary XML to well formated HTML by rules that you define. And, what’s really spiffy is that you can use XSLT to automatically generate AJAX code as well. However, there are a few tricks to know and a few kinks to watch out for.
XSLT position()
This function returns the element’s position in the tree. The thing to look out for? Whitespace is also an element! As such, if you’re using an <xsl:apply-transformation />, you want to make sure the select statement specifically lists the kind of node you want, and not just some parent element.
XSLT replace() is XSLT 2.0
Turns out some browsers have a problem with XSLT v2.0. Evil. Just evil. And, along this line, so are variables. Some of the really nice features of XSLT might not be possible.
Firefox Hangs When XSLT Generates Scriptaculous
The Scriptaculous effects library is very clever by being very modular. When you include it, dependencies allow just the pieces you want to load. This has the advantage of making the pages very light weight. It appears to do this feat of magic by injecting content into the DOM at the point you include the <SCRIPT>…</SCRIPT> tag. Only problem is, if the DOM is being generated on the fly by XSLT, bad things can happen. Surprisingly, this seems to be a Firefox-only problem — and I’ve reported the problem to the authors of Scriptaculous. If I get no response, I’m going to the Mozilla people next. IE does not appear to be affected, nor is Safari.
Containers Get Instances
I had some XSLT code which was building my arrays, and deferring initialization of sortable containers until page load completion time. The problem was, for some reason, the containers were not getting initialized at completion. The result was that certain elements weren’t functioning. When I tried initializing as I went, each container got an instance of the array. Follow that again slowly. Sortable containers don’t get a reference to an array, they get a copy of the array, and if the array (which contains all containers you’re allowed to interact with) isn’t fully initialized, your page is broken. Admittedly, a lot of this problem happened because the load order of things between libraries wasn’t clear. Each AJAX library usually hooks into the OnLoad() call, so you better not have one, but you’ll need to see if it put itself first, or last, in the chain.
That sums it up…
That sums up the mental core dump. If you happen to have any tidbits, trivia, or embarrassing corrections, I’d love to hear from you.
Sams: Teach yourself Ajax has a couple of nice examples in a very small quick reference guide. O’Reilly Safari link: http://safari.oreilly.com/0672328682 and it’s downloadable with one of the five download tokens they supply per month for your subscription. It has a few other code samples that fit nicely into an AJAX tool box.
Hi,
You mentioned not using innerHTML and that we should substitute “.text” and “.html”, how does this work? Could you point me to a reference?
PRINTED: Head Rush Ajax, page 183: “Using the innerHtml property is not a good way to get and set the contents of an element. It’s not part of the DOM specification, and the W3C has deprecated it…”
ONLINE: http://www.w3.org/TR/WCAG20-SCRIPT-TECHS/#SCR21
Aside from the possibility of not all browsers supporting it (IE6), the real gist of it is that the W3C has deprecated the .innerHTML property. That is, while it may work now, and it may still work tomorrow, one day in the future, it will get pulled. You can either ignore the problem and get bitten, or, switch to the DOM methodology and have your code work now and forever without worry.
AJAX libraries, however, are the solution. The switch is trivial, my preference is jQuery, and I suggest going to http://jquery.com/api/ to read about the function details. But, here’s how it works:
Imagine you have a div: <DIV ID=”mydiv”>Hello</DIV>
Using jQuery, one simply writes $(“#mydiv”).text() and it will return the string “Hello”.
If you wrote $(“#mydiv”).text(“Goodbye”), the DIV’s contents would magically change to Goodbye.
But what happens if you wrote: $(“#mydiv”).text(“<B>Greetings</B>”) ?
Well, because you used text(), the string will be properly escaped so the DIV literally shows the text “<B>Greetings</B>”.
If instead, you used $(“#mydiv”).html(“<B>Greetings</B>”), the DIV would have a B element created in the DOM, and its text would become Greetings in bold.
And, just like before, you can provide no arguments to get the content, although this time the HTML: $(“#mydiv”).html()
The point being is that the AJAX does the correct thing, for the correct browser, using the non-deprecated standard DOM method under the hood.
You simply don’t have to worry about it, and it’s just as easy.
document.getElementById(“mydiv”).innerHtml = “XYZZY”;
Becomes…
$(“#mydiv”).html(“XYZZY”);
And at that point, you never have to worry again.