By now the entire World has heard about AJAX, even those who don’t care about Web-Development have seen the potential of this new technology. Everybody is tired of endless introductions on how cool AJAX is and those endless lists of good examples like Google Suggest, GMail and alike, so I decided to cut a long story short and jump right into the real tutorial.
Is this tutorial any different from the others? Well yes and no, it is different in being a tutorial on how to design and build a complete site and not just some fancy little details like how to turn caching in AJAX off or how to create a fancy widget. To keep the tutorial readable, and to avoid having to implement low level functionality, I’m using the dojo toolkit, I tried prototype too and really enjoyed working with being a really nice and easy to use Library, but dojo provides much more functionality bundled with it. For both frameworks one thing is true: documentation is scarse, and I spent alot time debugging and reading posts on the newsgroups.
For debugging I suggest using Firefox Venkman and the really nice Firebug extension, which make AJAX a lot easier to understand, especially FireBug’s "Log each Request" Feature.
In this tutorial we will try to design a community portal as it has a wide range of different components that give a good overview of what is archievable with AJAX, also it should provide you with the basic tools that will help you in more complex applications.
To get back to this tutorial we will have several small modules that represent parts on the site, along with real modules we will have some virtual modules that take care of some functionality, but more on this later. Modules in this tutorial include:
var Engine = {
bootstrap: function(){
this.setStatus(“Loading…”);
dojo.require(“dojo.io.*”);
dojo.hostenv.writeIncludes();
if((“”+document.location).indexOf(‘#’) < 0){
Engine.loadPage(“#Page/Welcome”);
}else{
Engine.loadPage(“” + document.location);
}
},
This part is easy to understand it does nothing else than set the status to "Loading…", then include some dojo packages and then display a page using Engine.loadPage() as is shown later on. As you can see everything is bundled into a variable named "Engine" thus making it easy to reference it from outside. The bootstrap function is called by the following code in the page:<body onload=“Engine.bootstrap()”></body>
The next thing to do is to give the Engine the ability to load modules, this is done by downloading a JavaScript file that contains the Module (again encapsulated in its own namespace) and then calling init() of the Module which will initialize the module: loadedModules: new Array(),
modules: new Array(),
loadModule: function(module){
if(Engine.loadedModules[module])
return;
dojo.io.bind({
url: “javascript/” + module + “.js”,
sync: true,
error: function(type, evaldObj){Engine.showError(“Error while loading module.”);},
load: function(type, data, evt){
eval(data);
Engine.modules[module].execute(uri);
}
});
},
For performance issues we don’t want to download the modules more than once, that’s why we use the two arrays in the first two lines: loadedModules[] is an array of booleans which to every modulename tells us if it was loaded or is yet to be loaded, the second array contains references to the Modules themselfs (being variables they can be referenced like this). loadModules() itself does nothing fancy, it just issues a Synchronous XMLHTTPRequest to download the Module’s source code. It’s synchronous because we don’t want anything to happen at this stage, a call to a function that is not yet loaded for example, this gives us a certain security. Notice that the code is evaluated using eval(). uri: "/",
loadPage: function(url){
Engine.setStatus(“Loading…”);
if(!url)
url = “” + document.location;
var hashIndex = url.indexOf(‘#’);
if(hashIndex < 0 || hashIndex <= url.length-2)
return Engine.hideStatus;
uri = url.substring(hashIndex + 1);
var moduleLength;
if(uri.indexOf(‘/’) > 0)
moduleLength = uri.indexOf(‘/’);
else
moduleLength = uri.length;
var module = uri.substring(0,moduleLength);
uri = uri.substring(uri.indexOf(‘/’));
if(Engine.loadedModules[module] && ! dojo.lang.isUndefined(Engine.modules[module])){
Engine.modules[module].execute(uri);
}else{
Engine.loadModule(module);
}
},
The URI is there so other modules and function get work with it easily without having to parse it over again. As you can see loadPage() mainly interprets the URL. Determining the module to load is fairly easy being the first part of the Query string. Some may ask why we’re using URLs like "http://www.example.com/Page#This/is/a/long/string". This is because we don’t want to break the ability to bookmark the pages. AJAX itself does break the bookmarkability because everythin happens in a single page, whereas without AJAX every URL identified a single resource. We use the part behind the ‘#’ because the browser does not issue another request to the webserver, which would unload the entire AJAX application, yet we assign a resouce to a unique URL. bootstrap() also loads the requested page from the URL using loadPage(). Cutting a long story short: a user can browse through our site then copy&paste the URL somewhere and when he returns to the URL he will see exaclty the page he left. setStatus: function(message){
if($(’status’) != null){
$(’status’).parentNode.removeChild($(’status’));
}
var body = document.getElementsByTagName(“body”)[0];
var div = document.createElement(“div”);
div.style.position = “absolute”;
div.style.top = “50%”;
div.style.left = “50%”;
div.style.width = “200px”;
div.style.margin = “-12px 0 0 -100px”;
div.style.border = “0px”;
div.style.padding = “20px”;
div.style.opacity = “0.85″;
div.style.backgroundColor = “#353555″;
div.style.border = “1px solid #CFCFFF”;
div.style.color = “#CFCFFF”;
div.style.fontSize = “25px”;
div.style.textAlign = “center”;
div.id = ’status’;
body.appendChild(div);
div.innerHTML = message;
},
hideStatus: function(){
Engine.opacityDown($(’status’));
},
opacityDown: function(theElement){
if(theElement == null)
return;
var opacity = parseFloat(theElement.style.opacity);
if (opacity < 0.08){
theElement.parentNode.removeChild(theElement);
}else{
opacity -= 0.07;
theElement.style.opacity = opacity;
setTimeout(function(){Engine.opacityDown(theElement);}, 50);
}
return true;
},
setContent: function(content){
$(‘content’).innerHTML = content;
},
showError: function(message){
Engine.setStatus(message);
setTimeout(“Engine.hideStatus()”,10000);
}
}
This completes the engine. You can find the full script here.var Page = {
init: function(){
Engine.modules[“Page”] = Page;
Engine.loadedModules[“Page”] = true;
},
execute: function(uri){
try{
dojo.io.bind({
url: “resources” + uri + “.php”,
sync: false,
error: function(type, evaldObj){
Engine.showError(“Error while loading Content.”);},
load: function(type, data, evt){
Engine.setContent(data);
Engine.hideStatus();
});
}catch(e){
alert(e);
}
}
}
Page.init();
When the module is loaded it will register itself to the Engine (see the init() function) and the Engine will then call execute() which does nothing else than to load the page in the background and then display it in the content area. Easy isn’t it?联系客服