Level: Intermediate Jack D Herrington (jherr@pobox.com), Senior Software Engineer, Leverage Software Inc.
06 Mar 2007 Asynchronous JavaScript + XML (Ajax) was certainly the technology buzzword of 2006 and looks to do just as well or better in 2007. But what does it really mean for your application? And which common architectural patterns are used widely in Ajax applications? Discover five common Ajax design patterns that you can use as a basis for your own work.
Sure, Ajax is the Web 2.0 buzzword that everyone wants associated with their site. But
what does it really mean? And how are engineers integrating it into their sites at an
architectural level. In this article, I cover the basics of Ajax and show some Ajax
design patterns that have become proven best practice with Web 2.0 development.
To start, Ajax is just a buzzword that covers a set of technologies, including
Dynamic HTML (DHTML) and the XMLHTTPRequest object. DHTML
is a combination of three elements: Hypertext Markup Language (HTML), JavaScript code,
and Cascading Style Sheets (CSS). Using JavaScript code on a Web page, you can change
the page dynamically to add, remove, or change the content. That's the dynamic portion
of DHTML. JavaScript code uses the XMLHTTPRequest object to
request data from the server after the page has been loaded.
The combination of these two elements -- requesting data from the server on the fly and
changing the page to use the data -- is the essence of what's called Ajax and
the dynamic nature of Web 2.0 sites.
But that doesn't really tell you how these features are used in the real world and how
you should use them on your site. For that, you need a set of simple design patterns.
If you are unfamiliar with that term, it comes from the excellent book of the same name
(see Resources). That book provided a set of implementation
patterns for the common tasks that confront engineers. The book provided not just best
practices for how to design systems but also a terminology that engineers can
use to talk about their code.
This article presents five common Ajax design patterns. They vary in using HTML, XML, and
JavaScript code to get data from the server. I start with the simplest pattern, which is
to update your page with new HTML from the server.
Pattern 1. Replacing HTML segments
Perhaps the most common Ajax task is to request updated HTML from the server and
update a portion of the page with it. You can do this can periodically -- for
example, to update stock quotes. Or you can update on demand -- for example, in
response to a search request.
The code in Listing 1 requests a page from the server, and
then places that content into a <div> tag in the
body of the page.
Listing 1. Pat1_replace_div.html
<html>
<script>
var req = null;
function processReqChange() {
if (req.readyState == 4 && req.status == 200 ) {
var dobj = document.getElementById( 'htmlDiv' );
dobj.innerHTML = req.responseText;
}
}
function loadUrl( url ) {
if(window.XMLHttpRequest) {
try { req = new XMLHttpRequest();
} catch(e) { req = false; }
} else if(window.ActiveXObject) {
try { req = new ActiveXObject('Msxml2.XMLHTTP');
} catch(e) {
try { req = new ActiveXObject('Microsoft.XMLHTTP');
} catch(e) { req = false; }
} }
if(req) {
req.onreadystatechange = processReqChange;
req.open('GET', url, true);
req.send('');
}
}
var url = window.location.toString();
url = url.replace( /pat1_replace_div.html/, 'pat1_content.html' );
loadUrl( url );
</script>
<body>
Dynamic content is shown between here:<br/>
<div id="htmlDiv" style="border:1px solid black;padding:10px;">
</div>
And here.<br/>
</body>
</html>
|
Listing 2 shows the content that the code is requesting.
Listing 2. Pat1_content.html
HTML encoded content goes here.
|
When I load the page in Firefox, I see the result shown in
Figure 1.
Figure 1. The page with the
replaced <div> tag
Go back to the code in Listing 1 and look at a few
things. The first thing to notice is the loadUrl()
function, which requests a URL from the server. This function uses the
XMLHTTPRequest object to ask the server for the new
content. It also specifies a callback function -- in this case,
processReqChange -- that's called when the browser
has received the content.
The processReqChange function then inspects the object
to see whether the request has been completed. If it has, the function sets the
innerHTML of the <div>
tag in the page into the text of the response.
The use of the <div> tag as a placeholder for
dynamic content is a staple of Ajax code. These tags have no visible presence
(unless you add borders and such, as I have), but they act as a good marker for
where content should go. Engineers also use the <span>
tag for replaceable segments, as I demonstrate later. The difference between a
<div> and a <span>
tag is that the former imposes a line break (like a paragraph), while the latter
delineates a section of inline text.
Getting back to the processReqChange function for a
moment, it's important that the function check the value of both the
status and the readyState
value. While some browsers will call the function only when the request
is complete, other browsers will call back continuously to tell the code that the
request is still running.
The tabbed display variant
Another variant of this pattern is to create a tabbed style of display.
Listing 3 shows a simple tabbed Ajax interface.
Listing 3. Pat1_tabs.html
<html>
<script>
var req = null;
function processReqChange() {
if (req.readyState == 4 && req.status == 200 ) {
var dobj = document.getElementById( 'tabDiv' );
dobj.innerHTML = req.responseText;
}
}
function loadUrl( tab ) {
var url = window.location.toString();
url = url.replace( /pat1_tabs.html/, tab );
...
}
function tab1() { loadUrl( 'pat1_tab1_content.html' ); }
function tab2() { loadUrl( 'pat1_tab2_content.html' ); }
tab1();
</script>
<body>
<a href="javascript: void tab1();">Tab 1<a>
<a href="javascript: void tab2();">Tab 2<a>
<div id="tabDiv" style="border:1px solid black;padding:10px;">
</div>
</body>
</html>
|
Listing 4 shows the content for the first tab.
Listing 4. Pat1_tab1_content.html
And Listing 5 shows the content for the second tab.
Listing 5. Pat1_tab2_content.html
 | To see what this looks like in a live environment, view this online version of pat1_tabs.html . |
|
When I bring this page up in my browser, I see the first tab, as shown in
Figure 2.
Figure 2. The content for the
first tab
I then click the link for the second tab. The browser retrieves the second tab's
contents and shows it in the tab area, as shown in Figure 3.
Figure 3. The content for the
second tab
This is the quintessential use of this design pattern -- to take requests from
the user and update a portion of the display with the new material, in this
case, creating the illusion of a tabbed display. The value from the application
side is that you can downloaded a much lighter-weight page to customers, who can
then access the material they want on demand.
Before Ajax, the common technique was to have both tabs on the page, then hide
or show them on demand. This meant that the HTML for the second tab was
created even if it was never viewed, wasting both server time and bandwidth. With
this new Ajax method, the HTML for the second tab is created only when the user
requests it.
The read more variant
 | To see what this looks like in a live environment, view this online version of pat1_readmore.html . |
|
Yet another variation on this theme is the Read more link, as shown in
Figure 4.
Figure 4. The Read more link on my
boring blog entry
Suppose that I really wanted to read more about the continuing adventures of my dog
walk. I can click the Read more link and have that link replaced with
the complete engrossing story, as shown in Figure 5.
Figure 5. The page after clicking
the Read more link
The value for customers is that they get more material seamlessly without a page
refresh.
Listing 6 shows the code for this page.
Listing 6. Pat1_readmore.html
<html>
<script>
var req = null;
function processReqChange() {
if (req.readyState == 4 && req.status == 200 ) {
var dobj = document.getElementById( "moreSpan" );
dobj.innerHTML = req.responseText;
}
}
function loadUrl( url ) { ... }
function getMore()
{
var url = window.location.toString();
url = url.replace( /pat1_readmore.html/, 'pat1_readmore_content.html' );
loadUrl( url );
}
</script>
<body>
<h1>Walking the dog</h1>
I took my dog for a walk today.
<span id="moreSpan">
<a href="javascript: void getMore()">Read more...</a>
</span>
</body>
</html>
|
Listing 7 shows the content for the "read more" section.
Listing 7. Pat1_readmore_content.html
It was a nice day out. Warm and sunny. My dog liked getting out for a stretch.
|
This code demonstrates the use of the <span>
tag instead of the <div> tag. The approach
you use depends on the requirements of your user interface (UI). But as you
can see, it's easy to use either approach.
Getting new HTML for the page is one thing, but what about when you want the
JavaScript code on the page to actually do something intelligent with the
data. How do you get the data to the browser in a structured way? Why XML,
of course.
Pattern 2. Reading XML data
For some reason, Ajax has become synonymous with XML, even though XML isn't strictly
required. As you can see from the examples above, you can return straight text or
even fragments of HTML -- or Extensible HTML (XHTML) -- code. But sending XML can
have its rewards.
Listing 8 shows Ajax code that requests records about books
from the server, then displays that data in a table within the page.
Listing 8. Pat2_xml.html
<html>
<head>
<script>
var req = null;
function processReqChange() {
if (req.readyState == 4 && req.status == 200 && req.responseXML ) {
var dtable = document.getElementById( 'dataBody' );
var nl = req.responseXML.getElementsByTagName( 'book' );
for( var i = 0; i < nl.length; i++ ) {
var nli = nl.item( i );
var elAuthor = nli.getElementsByTagName( 'author' );
var author = elAuthor.item(0).firstChild.nodeValue;
var elTitle = nli.getElementsByTagName( 'title' );
var title = elTitle.item(0).firstChild.nodeValue;
var elTr = dtable.insertRow( -1 );
var elAuthorTd = elTr.insertCell( -1 );
elAuthorTd.innerHTML = author;
var elTitleTd = elTr.insertCell( -1 );
elTitleTd.innerHTML = title;
} } }
function loadXMLDoc( url ) {
if(window.XMLHttpRequest) {
try { req = new XMLHttpRequest();
} catch(e) { req = false; }
} else if(window.ActiveXObject) {
try { req = new ActiveXObject('Msxml2.XMLHTTP');
} catch(e) {
try { req = new ActiveXObject('Microsoft.XMLHTTP');
} catch(e) { req = false; }
} }
if(req) {
req.onreadystatechange = processReqChange;
req.open('GET', url, true);
req.send('');
}
}
var url = window.location.toString();
url = url.replace( /pat2_xml.html/, 'pat2_xml_data.xml' );
loadXMLDoc( url );
</script>
</head>
<body>
<table cellspacing="0" cellpadding="3" width="100%">
<tbody id="dataBody">
<tr>
<th width="20%">Author</th>
<th width="80%">Title</th>
</tr>
</tbody>
</table>
</body>
</html>
|
Listing 9 shows the data for the page.
Listing 9. Pat2_xml_data.xml
<books>
<book>
<author>Jack Herrington</author>
<title>Code Generation in Action</title>
</book>
<book>
<author>Jack Herrington</author>
<title>Podcasting Hacks</title>
</book>
<book>
<author>Jack Herrington</author>
<title>PHP Hacks</title>
</book>
</books>
|
 | To see what this looks like in a live environment, view this online version of pat2_xml.html . |
|
When I load the page in my browser, I see the result shown in
Figure 6.
Figure 6. The XML display page
The big difference between this page and the pages in the previous pattern is in the
processReqChange function. Instead of looking at
responseText, you now look at
responseXML, an XML Document Object Model (DOM) available
only if the response from the server was properly encoded XML.
Using responseXML, I request the list of
<book> tags from the XML document. I then get the
<title> and <author>
elements from each. Next, I add a row to the table for each book and cells to each
row to contain the author and title data.
This is a pretty rudimentary use of the XML data. More sophisticated JavaScript code
can perform client-side sorting or searching based on the returned data.
Unfortunately, the downside of transferring XML data is that it takes some time for
the browser to parse through the XML document. Also, the JavaScript code to find
the data in the XML can be complex (as seen in Listing 8). The
alternative is to request JavaScript code from the server.
Pattern 3. Reading JavaScript data
Requesting JavaScript data from the server is a technique that often goes by the
classy code name JavaScript Object Notation (JSON). The value of returning
JavaScript data is that it's efficient for the browser to parse and creates
JavaScript data structures, which are a lot easier to use.
Let me revise the code in Listing 8 that read XML from the
server to read JavaScript data from the server, instead. This new code is shown in
Listing 10.
Listing 10. Pat3_js.html
<html><head><script>
var req = null;
function processReqChange() {
if (req.readyState == 4 && req.status == 200 ) {
var dtable = document.getElementById( 'dataBody' );
var books = eval( req.responseText );
for( var b in books ) {
var elTr = dtable.insertRow( -1 );
var elAuthorTd = elTr.insertCell( -1 );
elAuthorTd.innerHTML = books[b].author;
var elTitleTd = elTr.insertCell( -1 );
elTitleTd.innerHTML = books[b].title;
} } }
...
|
All the HTML code remains the same. The processReqChange
function simply changes to read an eval that the
JavaScript data returned from the server. The function then uses the JavaScript
objects that come out of the eval as the source of the
data, which is then added to the table.
Listing 11 shows the JavaScript data from the server.
Listing 11. Pat3_js_data.js
[ { author: 'Jack Herrington', title: 'Code Generation in Action' },
{ author: 'Jack Herrington', title: 'Podcasting Hacks' },
{ author: 'Jack Herrington', title: 'PHP Hacks' }
]
|
 | To see what this looks like in a live environment, view this online version of pat3_js.html . |
|
It's easy to see why so many Ajax application engineers prefer to use JavaScript
code instead of XML to encode the data. The JavaScript code is easier to read
and manage as well as easier for the browser to process.
With all this data-gathering and display, you see that the key to Ajax is the
display of current data -- the important part there being current. So, how
do you ensure that you're always getting fresh data from the server?
Pattern 4. Avoiding browser cache
Browsers attempt to optimize Web traffic, so if you ask for the same URL twice, it's
likely that rather than request the page again, your browser will simply use the
page stored in the browser cache. So, another common pattern in Ajax applications
is the use of some randomizing element in the URL to ensure that the browser doesn't
return a cached result.
My favorite technique is to add the numeric value of the current time to the URL.
Listing 12 shows this technique.
Listing 12. Pat4_cache.html
<html>
<script>
...
function loadUrl( url ) {
url = url + "?t="+((new Date()).valueOf());
...
}
...
|
 | To see what this looks like in a live environment, view this online version of pat4_cache.html . |
|
This is the code from Listing 1 but with the addition of some
JavaScript text manipulation of the URL string. I append to the URL a new parameter
called t that has the value of the time. It doesn't
really matter whether the server recognizes the value. It's just a way to ensure that the browser ignores its URL-based page cache.
Pattern 5. Replacing multiple HTML segments
The final pattern I demonstrate is an advanced version of the first pattern: the
replacement of a <div> tag with content from the
server. A common problem in Web applications is that in response to user input,
several areas of the display must be updated. For example, in a stock quote
application, one part of the display might show the most recent quote, while
another portion of the display shows a list of the most recent values.
To update multiple areas of the display, I use an XML response from the server that
contains data for both sections. Then, I use a regular expression to break out the
individual sections from the response. Listing 13 shows this technique.
Listing 13. Pat5_multi_segment.html
<html>
<head>
<script>
var req = null;
function processReqChange() {
if (req.readyState == 4 && req.status == 200 ) {
var one = req.responseText.match( /\<one\>(.*?)\<\/one\>/ );
document.getElementById( 'divOne' ).innerHTML = one[1];
var two = req.responseText.match( /\<two\>(.*?)\<\/two\>/ );
document.getElementById( 'divTwo' ).innerHTML = two[1];
} }
function loadXMLDoc( url ) { ... }
var url = window.location.toString();
url = url.replace( /pat5_multi_segment.html/, 'pat5_data.xml' );
loadXMLDoc( url );
</script>
</head>
<body>
This is the content for segment one:<br/>
<div id="divOne" style="border:1px solid black;padding:10px;">
</div>
And segment two:<br/>
<div id="divTwo" style="border:1px solid black;padding:10px;">
</div>
</body>
</html>
|
Listing 14 shows the data from the server.
Listing 14. Pat5_data.xml
<segments>
<one>Content for segment one</one>
<two>Content for segment <b>two</b></two>
</segments>
|
When I load this code in my browser, I see the result shown in
Figure 7.
Figure 7. The two-segment display updated
with data from the server
In the page code, I could have used the XML response, as the material returned
from the server is valid XML. But it was easier to use regular expressions instead
to crack the individual segments from the XML code.
Conclusion
Ajax is as powerful as it is misunderstood and misused. The patterns I've shown in
this article provide a good jumping-off point for using Ajax in your Web
application. But in addition to using the code provided here, I recommend having
a look at some of the great Ajax and Web UI libraries that have come along with
the Web 2.0 revolution. Chief among these is the Prototype.js library, which
provides easy methods to get data to and from the server as well as
cross-browser-compliant methods to update Web page content. The value of using
these libraries is that dedicated engineers maintain and test them on a wide variety of browsers and platforms, which can save you a lot of work and headache.
Either way you cut it, Ajax as demonstrated by the patterns in this article is
something you should check into to add dynamic behavior to your applications.
Download | Description | Name | Size | Download method |
|---|
| Sample Ajax pattern files for this article | x-ajaxxml2_ajax_patterns.zip | 7KB | HTTP |
|---|
Resources Learn
- developerWorks XML zone: Learn all about XML at the developerWorks XML zone.
- Prototype.js: Try Prototype.js, a well-designed and well-implemented JavaScript library that provides cross-browser-compliant Ajax and DHTML objects. It's well worth your time to look at using Prototype.js instead of rolling your own Ajax JavaScript code.
- Design Patterns (Erich Gamma, et al., Addison-Wesley, 1995): Read the seminal book on the use of patterns in software engineering.
- IBM XML certification: Find out how you can become an IBM-Certified Developer in XML and related technologies.
- XML technical library: See the developerWorks XML Zone for a wide range of technical articles and tips, tutorials, standards, and IBM Redbooks.
- developerWorks technical events and webcasts: Stay current with technology in these sessions.
- Ajaxian: With this great resource, keep up with developments in Ajax and the front-end widgets that use it.
Get products and technologies
- Yahoo! UI library: Get the Yahoo! UI library, a Web UI kit that you can use along with Ajax in your Web applications.
- ATLAS library: Try the ATLAS library for Microsoft's take on an Ajax browser-compatibility layer. You don't need to use Microsoft® ASP.NET to make use of the library.
- Scriptaculous: Download this library of cross-browser effects and widgets that uses the Prototype.js library.
- Moo.fx: Get this lightweight widgets library that is based on Prototype.js.
Discuss
About the author
Rate this page
|