AJAX is a wonderful technology for making the user experience more gratifying. The usual use of AJAX is to pull some small piece of dynamic information from the server and populate a portion of your page with that information, rather than reload the entire page. Usually, the data you retrieve from the server is a small piece of text that you would insert into a form field or into an area of the page that requires a change. I'm going to show a technique that speeds up a page load, but uses AJAX in a kind of "cheating" way -- loading in entire HTML sections and simply writing them to the page. For the JavaScript/XHTML purist, this might be a cludge, but for the web developer who wants a simple way to speed up a page load in certain situations, it might be just the right technique.

AJAX

AJAX is asynchronous JavaScript and XML. We have many articles at Community MX that talk about AJAX, and indeed the latest version of Dreamweaver CS3 is AJAX enabled using the SPRY libraries. In this article, we'll ignore the XML part of the equation and simply use AJAX as a way to load in some content.

This specific application of AJAX will have a use when you have a page that loads in showing certain items, but also has certain items hidden. A typical example of this is a multi-tab web page, where the user loads the page in and sees the first tab, but has to click on the second tab in order to see any content. Why load the second page in before it's needed? We can load in the main web page, but leave the second tab as a blank <div> tag. The page will load in quicker and the second tab will load in using AJAX in the background while the user is digesting the first tab.

The technique happens very quickly, so there might not even be a discernable difference if you are on a high-speed connection. Users on dial-up will notice the initial page load is quicker. Depending on the size of the tab you are loading dynamically, the load time might be much quicker.

The HTML

Before I show the AJAX, lets create a simple HTML page that uses two tabs. This tab code would go somewhere in the body of the page:

<div id="divTabs">
<a href="javascript:;" onclick="tab(1);" id="tab1" title="Tab 1 ">Tab 1 </a>
<a href="javascript:;" onclick="tab(2);" id="tab2" title="Tab 2 ">Tab 2 </a>
<a href="javascript:;" onclick="tab(3);" id="tab3" title="Tab 3 ">Tab 3 </a>
</div>
<div id="divPages">
<div id="page1"> Stuff here </div>
<div id="page2"> More stuff here </div>
<div id="page3"> Even more stuff </div>
</div>

We'll need a little CSS in the head to hide the second and third tab, and show the links as tabs above the content:

<style type="text/css">
/* TAB stuff */
.show {margin-left: 1.5em;margin-bottom: 1em;padding-left: .5em;border-left: 1px solid #76685D;display: block;}
.hide { display: none;}
#divTabs {margin: 2em 0px -1px 5px;}
#divTabs a{font-weight: bold;text-decoration: none;padding: 3px 5px 2px 5px;border: 1px solid #425929;
background-color: #E7E4E2;color: #000;font-size: 14px; margin-bottom:-2px;}
#divTabs a:hover{background-color:#A1978F;color: #FFF;}
#divTabs a.current{border-bottom: 1px solid #FFF;background-color:#fff;color: #000;}
#divPages{margin: 2px 0px;padding: 20px;border: 1px solid #425929;}
#divPages div {display:none;}
div#page1 {display:block;}
</style>

Finally, we'll need a little JavaScript in the head to switch between tabs.

<script type="text/javascript">
// Set the current page in a global variable
var currentPage;

function tab(which) {
  var links = document.getElementById('divTabs').getElementsByTagName('a');
  for(var i=1; i <= links.length; i++) {
    links[i-1].className = (i==which) ? 'current' : '';
    eval("document.getElementById('page" + i + "').style.display='" + ((i==which) ? "block" : "none") + "'");
  }
   currentPage = which;
}
</script>

Since the focus of the article is on the AJAX, I won't discuss the basic tab system, but the bottom line is that you can use any tab system (including the SPRY Tabbed Panels object that ships with Dreamweaver CS3) that uses a <div> or other tag to contain the content of the tabs.

The Content

The content of the actual page is contained within the three main divs: page1, page2, and page3. Using JavaScript, we can address the innerHTML of these divs, by finding the element on the page using document.getElementById():

document.getElementById('page2').innerHTML = 'This is some dynamically written content';

If we were to put this statement in the body onload event, the page would load in and the second tab would contain the text we have dyamically written. The result would be seamless.


Figure 1: Dynamically written text

Let's take this to the next step and add the AJAX. Assume that the second tab will contain the following code:

<h2>AJAX</h2>
<p>AJAX is asynchronous JavaScript and XML. We have many articles at Community MX that talk about AJAX, and indeed the latest version of Dreamweaver CS3 is AJAX enabled using the SPRY libraries. In this article, we'll ignore the XML part of the equation and simply use AJAX as a way to load in some content.</p>
<p>This specific application of AJAX will have a use when you have a page that loads in showing certain items, but also has certain items hidden. A typical example of this is a multi-tab web page, where the user loads the page in and sees the first tab, but has to click on the second tab in order to see any content. Why load the second page in before it's needed? We can load in the main web page, but leave the second tab as a blank &lt;div&gt; tag. The page will load in quicker and the second tab will load in using AJAX in the background while the user is digesting the first tab.</p>
<p>The technique happens very quickly, so there might not even be a discernable difference if you are on a high-speed connection. Users on dial-up will notice the initial page load is quicker. Depending on the size of the tab you are loading dynamically, the load time might be much quicker.</p>

We can save this content as page2.html -- in a blank page, by itself with no <html> or <body> tags. The file essentially acts as an include file, but the including will happen after the page loads rather than during the serving of content by the web server as is typical with an include. Finally, we'll save the following as page3.html:

<form id="form1" name="form1" method="post" action="">
<table width="200" border="1">
<tr>
<td>Name:</td>
<td><input type="text" name="fullname" id="fullname" /></td>
</tr>
<tr>
<td>Email:</td>
<td><input type="text" name="email" id="email" /></td>
</tr>
<tr>
<td>Comments:</td>
<td><textarea name="comments" cols="80" rows="8" id="comments"></textarea></td>
</tr>
<tr>
<td></div></td>
<td><input type="submit" name="button" id="button" value="Submit" /></td>
</tr>
</table>
</form>

It's a simple contact form. Again, save the html code as if this were an include file -- no other tags or text in the file.

Now, we'll add the main worker in the AJAX functionality -- a function called processRemote(), which takes two arguments:

The function is versitile in that it can be used by any AJAX functionality on your page. The callback function makes that possible -- the only functionality included in the processRemote() function is the call to the remote server to grab the results. What is done with the results is handled by your callback function. The code for processRemote() follows:

function processRemote(url,callback) {
  var req = false;
  if (window.XMLHttpRequest) {
    req = new XMLHttpRequest();
  } else if (window.ActiveXObject) {
    req = new ActiveXObject("Microsoft.XMLHTTP");
  }
  try {
     req.open("GET", url, true);
     req.onreadystatechange = function() {
       // only if req shows "loaded"
       if (req.readyState == 4) {
         // only if "OK"
         if (req.status == 200) {
           var temp = new Array();
           temp.push(req.responseText);
           callback.apply(callback,temp);
         } else {
           alert("There was a problem retrieving the XML data:\n" + req.statusText);
         }
       }
    };
    req.send(null);
  }catch(e) {
     alert(e.message);
  }
}

If you've used AJAX, you've probably seen this type of code before. The only unique thing about it is this line:

callback.apply(callback, temp);

This is using the second argument -- the callback function -- and actually executing it by using the apply() method. All functions have this built-in method that allows you to call the function dynamically.

Now, we need a function that will be used to call our AJAX functionality to actually load the pages. We'll just call it getPage():

function getPage(page, tab) {
  var callback = function(result) {
    document.getElementById(tab).innerHTML = result;
  }
  processRemote(page, callback);
}

The variable callback is defined first and is a simple function definition -- this function will execute not here, but within the processRemote() function when the response comes back from the server. We simply define it here and pass it to the other function. Then we call the function for page 2 and page 3 in turn, in the body onload event:

<body onload="tab(1);getPage('page2.html','page2');getPage('page3.html','page3')">

When you browse to the page, the page will load in and show the first tab. At this point, tabs 2 and 3 are empty. The page is loaded, and after it is loaded, the second and third tabs load in the background. If these were larger pages, the effect would be noticiable.


Figure 2: The second tab loads in dynamically


Figure 3: The third tab is the form, loaded dynamically

Note that I have been using static content, but the pages might just as easily contain ColdFusion, PHP, ASP or other code. They could be recordset results, dynamic forms and tables, or information specific to a user. You could just as easily call the AJAX function like this:

page2.cfm?id=someOrderId

On page2.cfm, you would load in a user's order based on the order id you pass to the function. The possibilities are endless.

Conclusion

This article showed one specific use of AJAX -- loading fully-formatted HTML dynamically. There are many other more sophisticated uses of AJAX, but sometimes a little cheat can make a page load faster and more efficiently.