“Libraries, like Prototype and jQuery, abstract all this away…”
Let’s say that you’ve got a basic understanding of
JavaScript, you roughly know what
AJAX is, and you can twiddle the
DOM, but now it’s time for the rubber to meet the road and you want to get up to speed, know about the quirks, and learn hidden tidbits that come from head bludgeoning against the wall experience.
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;
}
}
}
Note:
readyState is a read-only property – you can’t set it.
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.
Note: if you find yourself calling getElementById(), you need to look at the $() function of these libraries. And, yes, a dollar sign is a legal identifier character, making a lone dollar sign a valid variable or function name.
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.
Other useful values: document .documentElement (the root node), .parentNode, .childNodes, .firstChild , .lastChild, .nodeType* , .nodeName, .nodeValue , .getAttribute(), .setAttribute() .
* 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.
Note: the onclick property of a DOM object is all lowercase, not camel-cased.
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.
Note: An HTML ID can start with a letter, dollar sign, or underscore. After that you can uses numbers, periods, and dashes. IDs are case sensitive, and though their technical size limit is 64K in size (wow!), though don’t count on your browser to honor that. Long IDs can make things slow and chew up memory.
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
See the benefits of XSLT. Download, unzip, and drag the .XML file into your browser.
XSLT_Example.zipThis example separates content, structure, and presentation.
But let’s discuss XSLT in the contents of an entire page.
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.