Concurrent AjaxFiled: Thu, Nov 30 2006 under Programming|| Tags: ajax javascript oop The problem with almost every AJAX tutorial you find on the net is that the examples they give are procedural. That is... It works until you need to make more than one AJAX call. If you try to use one function to do two calls, it breaks because the tutorial functions are geared to do one request at a time. To do multiple requests you'll end up copying and pasting the same AJAX routine over and over and over, renaming it each time. And with many versions of the AJAX tutorials, there are MULTIPLE functions that must be copied and renamed resulting in an absolute spaghetti code horror show. Fortunately Javascript supports objects and that makes writing a robust AJAX routine extremely easy. In this article I'll show you how to create an AJAX object in javascript, allowing you to bind an AJAX object to a specific layer in your HTML as easily as you can declare a new variable in javascript. I'll show you how to create hidden layers to store data, and how to process that data after you get it from the server. Lets get right to the code. The first order of business is to get our constructor object built.
function ajaxObject(layer, url) { // This is the object constructor
var that=this; // A workaround for some javascript idiosyncrocies
var updating = false; // Set to true if this object is already working on a request
this.callback = function() {} // A post-processing call -- a stub you overwrite.
this.update = function(passData) { // Initiates the server call.
if (updating==true) { return false; } // Abort if we're already processing a call.
updating=true; // Set the updating flag.
var AJAX = null; // Initialize the AJAX variable.
if (window.XMLHttpRequest) { // Are we working with mozilla?
AJAX=new XMLHttpRequest(); // Yes -- this is mozilla.
} else { // Not Mozilla, must be IE
AJAX=new ActiveXObject("Microsoft.XMLHTTP"); // Wheee, ActiveX, how do we format c: again?
} // End setup Ajax.
if (AJAX==null) { // If we couldn't initialize Ajax...
alert("Your browser doesn't support AJAX."); // Sorry msg.
return false // Return false (WARNING - SAME AS ALREADY PROCESSING!)
} else {
AJAX.onreadystatechange = function() { // When the browser has the request info..
if (AJAX.readyState==4 || AJAX.readyState=="complete") { // see if the complete flag is set.
LayerID.innerHTML=AJAX.responseText; // It is, so put the new data in the object's layer
delete AJAX; // delete the AJAX object since it's done.
updating=false; // Set the updating flag to false so we can do a new request
that.callback(); // Call the post-processing function.
} // End Ajax readystate check.
} // End create post-process fucntion block.
var timestamp = new Date(); // Get a new date (this will make the url unique)
var uri=urlCall+'?'+passData+'×tamp='+(timestamp*1); // Append date to url (so the browser doesn't cache the call)
AJAX.open("GET", uri, true); // Open the url this object was set-up with.
AJAX.send(null); // Send the request.
return true; // Everything went a-ok.
} // End Ajax setup aok if/else block
}
// This area set up on constructor calls.
var LayerID = document.getElementById(layer); // Remember the layer associated with this object.
var urlCall = url; // Remember the url associated with this object.
} // End AjaxObject
As you can see this code strongly resembles most every other AJAX routine you'll find on the Internet. The difference is that a few tweaks have been made to make it into an object. Don't worry though, it's very, very easy to use and understand. To use this code all you need is a url to your server-side script, setup a layer in the HTML, and create the object. I'll give you a snippet of PHP code at the end for the server-side processing but for now just assume that if we have a url of showtext.php?doc=helloWorld.txt then the php program on the server will look for a file called helloWorld.txt and return the contents of that file. So now lets dive in and set up the web-page. First we need a HTML layer to receive the data. <span id='helloContainer'></span> That's pretty much it. Now either with an onload event in the <body> tag, or just another javascript block at the end of the HTML file we just set up the constructor.
<script language="JavaScript" type="text/javascript">
<!--
var helloWorld = new ajaxObject('helloContainer', 'showtext.php');
//-->
</script>
With that one little variable declaration we've bound helloWorld to the 'helloContainer' span in the HTML and the showtext.php program on the web server. Now anytime we do....
helloWorld.update('doc=helloWorld.txt');
The web page is going to call the server, ask for helloWorld.txt (or whatever filename we pass) and it's going to
display the contents wherever the helloContainer span happens to be in your HTML. It will update ONLY that little
layer and it will do it without leaving the web page.
That's where most tutorials leave off, but since ajaxObject is a constructor, we can create a new layer... let's say... <span id='goodbyeContainer'></span> Now we update our onLoad (or end of HTML) javascript...
<script language="JavaScript" type="text/javascript">
<!--
var helloWorld = new ajaxObject('helloContainer', 'showtext.php');
helloWorld.update('doc=helloWorld.txt');
var goodbyeWorld = new ajaxObject('goodbyeContainer', 'showtext.php');
goodbyeWorld.update('doc=goodbyeWorld.txt');
//-->
</script>
Now whenever we call goodbyeWorld.update('doc=goodbyeWorld.txt') it's going to display goodbyeWorld.txt in the goodbyeContainer span. You can have both goodbyeWorld and helloWorld updating at the exact same times, as many objects as you want really. You can even have arrays of objects if you have a lot of layers you need to associate with AJAX calls. 1 1 Presently FireFox and IE have a 2 connection limit on ajax calls. You can set up as many calls as you want but the browser will only work on 2 at a time and the other requests will just wait in the queue until its their turn. The update method will return false if that object is already updating. Using the above examples, lets assume your webserver is running on a 286 and slashdot and digg are linking to it (IE, it's running really, really slow). You call helloWorld.update('file.txt') and it starts getting file.txt for it's layer. You call goodbyeWorld('anotherfile.txt') and it starts to get it's file for it's layer. Both helloWorld and goodbyeWorld are both off doing their thing, all is good. Now if you call goodbyeWorld again, before the first request has come back from the server, goodbyeWorld.update('file.txt') is going to return false and it's not going to do the last thing you asked for because it's still working on the first. If the request went through ok, it will return true. Another thing to note is that if the user's browser does not support ajax update will always return false just like there is already another ajax request in progress for that object. So that's a catch you need to be aware of. If you absolutely positively need to make sure the user has an ajax capable browser, you'll need to code a seperate check either in the object itself or a seperate check when the page is first loaded. The ajax object will not do it for you. Now we're going to kick it up a notch and show you the REAL power of this object. You know you can create as many layers as you want, and create AJAX objects for each of those layers as easily as you can create a javascript variable. Where things get different is that the layers don't have to be visible, and there's a way to access the data you get back from the server, and process it. First lets set up a hidden layer. <span id='abstract' style='display:none'></span> Next we need to let the object know what to do when it has finished retrieving data from the server. When an AJAX call is finished the ajaxObject puts the data it gets back in your layer -- in this case "abstract" -- and then it calls "callback" in the object. Now this starts out being a null function. It's there but it doesn't really do anything. We need to change that.
var liveData = new ajaxObject('abstract', 'showtext.php');
liveData.callback = function() { processData(); }
Here we've created a new object called liveData and associated it with the 'abstract' layer and 'showtext.php' as the server program. Then we've overwritten the callback function with something new. When liveData is done getting the data from the server it's going to call the callback function and now callback is going to call your function -- processData (which is not inside the object). processData will look something like this...
function processData() {
layerID = document.getElementById('abstract');
data = layerID.innerHTML;
// rest of code processes data which is just a giant string containing all the data recieved from the server.
}
Remember, we only overwrote callback for livedata, both helloWorld and goodbyeWorld are still calling their own respective (and empty) callback procedures, they won't be calling processData unless you explicitly tell them to. So to recap, we created a hidden layer, then setup a callback function. We call update, the server data goes into the hidden layer, then callback is called which then calls our post-processing function which puts all the stuff in the hidden layer into a string. Then you can process that data however you wish, outputting the data to new layers even doing other AJAX calls. There's really no limits. And now the showtext.php program for the server.
<?php
header("Cache-Control: no-cache, must-revalidate"); // Must do cache-control headers
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); // or IE will pull from cache 100% of time (which is really bad)
readfile($doc); // send file to browser
// $doc is whatever was passed as doc= in the url.
// showtext.php?doc=hello.txt will open hello.txt.
?>
Notes and caveats:
|