Ajax for Everyone (beyond hello world)Filed: Tue, Jan 09 2007 under Programming|| Tags: ajax intermediate warnings caveats OOP object So you've cut your teeth on an Ajax "Hello World!" tutorial and you're chomping at the bit to write your first application! Good for you! But before you begin here are a tips to consider and a tool for your box. Do you really, really need AJAX?The first thing you should do is to seriously question whether you need Ajax at all. One of the worst possible things you can do is to use Ajax to replace traditional site navigation. If you're using Ajax to replace hyper-link transitions, you're breaking the back-button and more than likely, confusing the user who has no clue what is happening. Ajax calls also don't start the browser's activity indicators, sowing even more confusion amongst your visitors. If you MUST do in-page navigation stick to frames and iframes -- they will at least preserve the back button and give the user a visual clue as to what is happening. Cookie MonstersBefore you write a single line of Ajax code you should take a good hard look at your site's cookies. Now I know that sounds weird but every single time the user opens a connection to your web server it sends your site's cookies. It does this for web pages, external javascript and stylesheets, even images, and it does it with each and every Ajax call. So if you're planning on writing an application which makes frequent calls to the server -- say an as-you-type field selector which fills in possible values based on what the user has already typed -- then your site simply can't be bumping up to the 4k size-limit because every time your user hits a key he'll be transmitting that 4k cookie. Not a huge issue on broadband, but a possible dealbreaker for dialup. Third party scripts such as bulletin boards also load up your site's cookies, so even if you think you know everything your site is doing it wouldn't hurt to make a blank web page with the following code alert(document.cookie); This will pop-up an alert box showing all the cookies your site is saving. If you can't change your site's cookies, then considering moving your application to a new domain so you're starting with a blank slate. Design for stacked server requestsRFC2616, which is the official reference for HTTP/1.1, specifies in section 8.1.4: Clients that use persistent connections SHOULD limit the number of simultaneous connections that they maintain to a given server. A single-user client SHOULD NOT maintain more than 2 connections with any server or proxy. What this means is that standards compliant browsers will allow only two Ajax connections at a time at most. Ironic as it may seem, Internet Explorer complies with this standard. Firefox does as well. Opera, in its never-ending goal to be the fastest browser on the internet allows more than two connections. For your application, it means if you open many simultaneous Ajax requests only one or two will be processed immediately and the rest will just patiently wait in line until a connection is available and they can be processed. To get around this, design your systems so that one Ajax call can send more than one request, and process more than one response in the callback. For instance if you have a 10 RSS feeds on your page -- when you first go to set up the page -- instead of making 10 separate Ajax calls, have a special routine which will ask the server for all 10 of the feeds with one request, the server will then pass back the 10 feeds you requested as one large data file which you can break out and fill for the initial state. This effectively sidesteps the 2 request limit and will substantially increase your application's performance. Long-story-short: wherever possible combine as many tasks as possible into one single task to keep the number of separate requests to an absolute minimum at all times. Ajax is not secureAjax is vulnerable to a prototyping attack where a malicious script can overwrite the XMLHTTP object with its own methods and properties, intercepting all data to and from the server and transmitting a copy of that communication to a third party. The risk of this is fairly small, but it does exist. So if you were planning to use ajax to pass along sensitive information like social security numbers, credit card numbers, or anything along those lines, you should very heavily reconsider this approach. Note that this vulnerability bypasses even a secured, encrypted webpage because the attack happens before the data is actually transmitted by the browser to the server. So stick to tried and true HTML forms if you're transmitting sensitive data. Don't DIGG yourself into a hole.The Digg/Slashdot/Reddit/(other big sites here) effect is well known. You get a front page link and suddenly thousands of people are hitting your site and your server is dealing with tens of thousands of simultaneous connections -- bringing your server to its virtual knees. With ajax you can bypass the major news aggregators and do this without the need for tens of thousands of visitors, you can do it with just a few handful. You've probably seen the heatmap reports which show where the user's mouse hovers on the screen. The first impulse is to send an ajax request whenever the user moves the mouse, you don't even need any data back, just send a timestamp, and the mouseX and mouseY. As soon as the data is sent, send a new report. Just tweak a few ajax headers to close the connection after the data is sent and you're golden. Well that's fine for one or two users, but the activity between the client and server is greatly increased as you add more and more users. It's like having a user who does nothing but hit the page refresh over and over putting a strain on the server equal to 10-20 regular visitors. add a second person and the load is now equivalent to 20-40 regular pages, 100 Ajax pages would be a load of 1000-2000 people, and it's actually worse than that because the longer the person stays the greater his or her load on the server. So VERY strongly consider the impact your proposed application will have on your webserver. Instead of opening an ajax request for each tiny request, cache them in the browser then send a larger chunk every few seconds instead of a tiny chunk every thousandth of a second. The Object LessonThe tutorials you have probably seen up to now probably have shown you an Ajax function. This is fine. It's easy to see how a function works. If you were wanting to do more than a single Ajax call at a time however you will find the "hello world" approach breaks pretty quickly. The solution to your problem is to set up a small object to handle your requests. Don't panic though! This is super easy to set up and to understand.
function ajaxObject(url) { // This is the object constructor
var that=this; // A workaround for some javascript idiosyncrocies
this.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 (that.updating==true) { return false; } // Abort if we're already processing a call.
that.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...
return false; // Return false (WARNING - SAME AS ALREADY PROCESSING!)
} else {
AJAX.onreadystatechange = function() { // When the browser has the request info..
if (AJAX.readyState==4) { // see if the complete flag is set.
that.updating=false; // Set the updating flag to false so we can do a new request
that.callback(AJAX.responseText,AJAX.status); // Pass respons and status to callback.
delete AJAX; // delete the AJAX object since it's done.
} // 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 urlCall = url; // Remember the url associated with this object.
} // End AjaxObject
And that is an ajax object. Here's an example of how to use it.
var sciFiFeed = new ajaxObject('http://www.server.com/feedhandler.php'); // Create object
sciFiFeed.callback = function (responseText, responseStatus) { // What to do with the data
displayFeed(responseText, responseStatus); // Your function here
} // End in-line function
sciFiFeed.update('feed=sciFi'); // Make the ajax call.
The first line creates a new Ajax object for our javascript to use and we pass it the url the object will call (in this case feedhandler.php) Next we overwrite the object's callback function and tell it to pass the responseText and responseStatus from the server to our own displayFeed function. Finally we make the call itself passing the data "feed=scifi" to feedhandler.php. When the browser has received the request it will call displayFeed and responseText will be the data the server sent and responseStatus will be be the status of the request (200=good, anything else and you probably have problems in responseText). Here's a template for displayFeed which is just a totally ordinary function.
function displayFeed(responseText, responseStatus) {
if (responseStatus==200) {
someDivision.innerHTML=responseText;
} else {
someDivision.innerHTML='Failed to get the feed you requested';
}
}
Now since ajaxObject is an object, we can define a new feed as simple as this.
var bookFeed = new ajaxObject('http://www.server.com/feedhandler.php');
bookFeed.callback = function (responseText, responseStatus) {
displayFeed(responseText, responseStatus);
}
bookFeed.update('feed=books');
This looks eerily similar to sciFiFeed and indeed it should. Since they're both requesting RSS feeds they both call feedhandler. Presumably the responseText from the server tells displayFeed the difference between the sci-fi feed and the book feed, in any case they both process the data in the same manner. bookfeed.update however passes 'books' to the server instead of 'sciFi'. Now, elsewhere in our code we can have stuff like this... <span onClick='sciFiFeed.update("feed=sciFi")'>Update Sci-Fi Feed</span>
<span onClick='bookFeed.update("feed=books")'>Update Book Feed</span>
If you have rows and rows of clickable links like this, it wouldn't matter one bit if the user sat down and clicked them all as fast as he could, they will all get updated. Your new object also has an updating property so you can see if the object is already busy. For instance bookFeed.updating will be true if bookFeed is currently waiting for data back from the server, and false if it is not. The only real caveat here is that if you depend on Ajax, you should do a separate check to make sure the browser can handle it. ajaxObject will return false if the user doesn't have Ajax, but it will also return false if that particular object still has an outstanding call to the server as well. Here's a small function that will check if the user has AJAX for you.
function checkAjax() {
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"); // Initialize with active X
} // End setup Ajax.
if (AJAX==null) { // If we couldn't initialize Ajax...
return false; // Return false
} // End check if AJAX=null
delete AJAX; // Delete the object since we don't need it anymore
return true; // Return True, we can do AJAX.
}
Here checkAjax will return true if the browser can handle Ajax and false if it can't. And that's the checklist for your first Ajax application. Now that you know the pitfalls and have the proper tools, the only thing you'll be needing are a few Ajax activity indicators. Good luck and happy coding!
|