When people think of Ajax and Web 2.0, they mostly remember the visual elements of the user experience. It's the feel of working in-place, without the page refresh, that gives Ajax its distinctive appeal. It's not completely hype: The page refresh of traditional HTML applications does cause a blink and a reload that even on the fastest connections presents a visual context change.
In this article, I show several techniques—both with Ajax and without—that demonstrate this context change-free approach to user experience design. I start with the simplest example of Ajax user experience, the tabbed window.
Tabs present the easiest way to put a lot of data in a relatively small amount of real estate. And the fantastic Prototype.js JavaScript library makes building Ajax-enabled tabbed windows in Dynamic HTML (DHTML) ridiculously easy, as shown in Listing 1. To learn more about the Prototype library and download a copy of it for your use, see Resources.
Listing 1. index.html
<html>
<head>
<script src="prototype.js"></script>
</head>
<body>
<a href="javascript:void loadTab( 'tab1.html' )">Tab 1</a> |
<a href="javascript:void loadTab( 'tab2.html' )">Tab 2</a> |
<a href="javascript:void loadTab( 'tab3.html' )">Tab 3</a>
<div id="content" style="padding:5px;border:2px solid black;">
</div>
<script>
function loadTab( tab ) {
new Ajax.Updater( 'content', tab, { method: 'get' } );
}
loadTab( 'tab1.html' );
</script>
</body>
</html>
|
At the top of the file, I include the Prototype.js library, which handles all the
Ajax work. Then, you see a list of links to the various pages, each of which calls
loadTab to update the content area of the page. The
content area is a <div> with the ID
content. The loadTab function
calls the Ajax.Updater to update the content
<div> with the specified HTML file.
Listing 2 shows the first tab HTML file. All the other files are similar to this one.
Listing 2. tab1.html
Tab 1
|
When I navigate to the file in my browser, I see something like Figure 1.
Figure 1. The starting tab page
Then, when I click the second tab, I see the content shown in Figure 2.
Figure 2. After clicking the second tab
You can achieve this functionality by using hidden <div>
tags with the content, then switching out their visibility. The advantage of the
Ajax approach is that the content is created and loaded only when necessary. So,
the compute time to build the content is reduced or eliminated if users choose
not to see the content.
The next three examples are all ways to deal with tables using Ajax techniques (or something that looks like Ajax).
My first demonstration of building tables with Ajax is through the use of an XML request to the server over Ajax. The value of this technique is two-fold. First, it loads the data on demand and can be updated in place, which makes for a pleasant user experience. Second, the technique requires an XML data source, which is valuable not just for the Ajax code but for any client looking to consume your data through XML.
Listing 3 shows the XML-driven table example code.
Listing 3. index.html
<html>
<head>
<script src="prototype.js"></script>
</head>
<body>
<table id="books">
</table>
<script>
new Ajax.Request( 'books.xml', {
method: 'get',
onSuccess: function( transport ) {
var bookTags = transport.responseXML.getElementsByTagName( 'book' );
for( var b = 0; b < bookTags.length; b++ ) {
var author = bookTags[b].getElementsByTagName('author')[0].firstChild.nodeValue;
var title = bookTags[b].getElementsByTagName('title')[0].firstChild.nodeValue;
var publisher =
bookTags[b].getElementsByTagName('publisher')[0].firstChild.nodeValue;
var elTR = $('books').insertRow( -1 );
var elTD1 = elTR.insertCell( -1 );
elTD1.innerHTML = author;
var elTD2 = elTR.insertCell( -1 );
elTD2.innerHTML = title;
var elTD3 = elTR.insertCell( -1 );
elTD3.innerHTML = publisher;
}
}
} );
</script>
</body>
</html>
|
The onSuccess handler of the
Ajax.Request call breaks up the XML data by first
getting each book element. Then, it gets the author,
title, and publisher values
from the nested tags. Finally, it adds the data to the table using
insertRow and insertCell for
each book.
Listing 4 shows the XML code for this example.
Listing 4. books.xml
<books>
<book>
<author>Jack Herrington</author>
<title>Code Generation In Action</title>
<publisher>O'Reilly</publisher>
</book>
<book>
<author>Jack Herrington</author>
<title>Podcasting Hacks</title>
<publisher>O'Reilly</publisher>
</book>
<book>
<author>Jack Herrington</author>
<title>PHP Hacks</title>
<publisher>O'Reilly</publisher>
</book>
</books>
|
Figure 3 shows the resulting DHTML file.
Figure 3. The XML-driven table page
This example is fine for small result sets, but what about for something larger?
To create paged tables with Ajax, I show a slightly different technique. Not because I necessarily need to—I can easily extend the XML technique shown above—but I want the examples in this article to show a variety of techniques from which you can pick and choose to suit the needs of your application.
In this case, I use a PHP data source that provides blocks of HTML code for the various pages of a paged table. The HTML code starts in Listing 5.
Listing 5. index.html
<html>
<head>
<script src="prototype.js"></script>
</head>
<body>
<div>
<a href="javascript:void updateTable(0);">1</a> |
<a href="javascript:void updateTable(1);">2</a> |
<a href="javascript:void updateTable(2);">3</a> |
<a href="javascript:void updateTable(3);">4</a> |
<a href="javascript:void updateTable(4);">5</a> |
<a href="javascript:void updateTable(5);">6</a>
</div>
<div id="states">
</div>
<script>
function updateTable( start ) {
new Ajax.Updater( 'states', 'stats.php?start='+(start*10)+'&count=10',
{ method: 'get' } );
}
updateTable( 0 );
</script>
</body>
</html>
|
The majority of the page is in the links to the various pages of the table. Then,
the states <div> tag receives the content of the
table from the stats.php page.
To run a multi-paged example, I need a larger data set, so I found some United
States population statistics and created a PHP function called
getdata, which returns an array of states and their
populations over the years. Listing 6 shows an excerpt of the function.
Listing 6. data.php
<?php
function getdata()
{
$population = array();
$population []=
array( 'Alabama',4447100,4527166,4596330,4663111,4728915,4800092,4874243 );
$population []= array( 'Alaska',626932,661110,694109,732544,774421,820881,867674 );
...
$population []= array( 'Wyoming',493782,507268,519886,528005,530948,529031,522979 );
return $population;
}
?>
|
Now, to get various portions of the data, I create a stats.php page that, given a start and end index, returns an HTML table with just that portion of the data. Listing 7 shows this file.
Listing 7. stats.php
<table>
<?php
require 'data.php';
$data = getdata();
$start = 0;
$count = count( $data );
if ( array_key_exists( 'start', $_GET ) ) $start = $_GET['start'];
if ( array_key_exists( 'count', $_GET ) ) $count = $_GET['count'];
$index = 0;
foreach( $data as $state ) {
if ( $index >= $start && $index < $start + $count ) {
?>
<tr>
<?php
foreach( $state as $item ) {
?>
<td><?php echo($item); ?></td>
<?php
}
?>
</tr>
<?php
}
$index += 1;
}
?>
</table>
|
When I bring up the page in my browser, as in Figure 4, it loads the first page automatically.
Figure 4. The paged table display
As I click the various sections of the table, the table from stats.php loads dynamically. Figure 5 shows what happens when I click the second page of the data.
Figure 5. The second page of the paged table display
This technique is similar to the one I used with the tabs. Passing HTML over Ajax is often as effective as XML. And it's a bit easier on the browser, because the XML doesn't need to be parsed and converted back into HTML again.
The third table-building technique I show is the easiest non-Ajax way to build a table dynamically.
The page in Listing 8 shows a simple technique to create paged tables without Ajax for the back end.
Listing 8. index.php
<html>
<head>
<script src="prototype.js"></script>
</head>
<body>
<div>
<?php
require( 'data.php' );
$data = getdata();
$states = count( $data ) / 10;
for( $s = 0; $s < $states; $s++ ) {
?>
<?php echo( ( ( $s == 0 ) ? '' : '| ' ) ); ?>
<a href="javascript:void updateTable(<?php echo( $s ) ?>);">
<?php echo( $s + 1 ) ?>
</a>
<?php
}
?>
</div>
<?php
$index = 0;
foreach( $data as $state ) {
?>
<?php
if ( $index % 10 == 0 ) {
?>
<?php if ( $index > 0 ) { ?></table></div><?php } ?>
<div id="states<?php echo( $index / 10 ) ?>" style="display:none;">
<table>
<?php
}
?>
<tr>
<?php
foreach( $state as $item ) {
?>
<td><?php echo($item); ?></td>
<?php
}
?>
</tr>
<?php
$index += 1;
}
?>
</table>
</div>
<script>
function updateTable( id )
{
var elStateDivs = [];
<?php
for( $s = 0; $s < $states; $s++ ) {
?>
elStateDivs.push( $( 'states<?php echo( $s ) ?>' ) );
<?php
}
?>
for( var i = 0; i < elStateDivs.length; i++ ) {
if ( i == id ) elStateDivs[i].show();
else elStateDivs[i].hide();
}
}
updateTable( 0 );
</script>
</body>
</html>
|
In this case, I use the PHP code to create a series of <div>
tags—one for each page in the table. The first one is shown by default,
while the rest are hidden. The updateTable function
shows or hides the various portions of the table depending on which page is
selected.
Notice that I still include the Prototype.js library—not because of the
Ajax support. I want to make it easier to get a hold on each
<div> element using the $()
method and the show and hide
methods, all of which the library implements.
Figure 6 shows the page.
Figure 6. The mock paged table display
My last Ajax example shows a glider control. A glider is a new form of display that provides a dynamic left/right motion as the pages of the display change.
To enable the glider effect, I need a few more libraries. The first library is the Scriptaculous library, which is built on Prototype.js. It provides the effects that the glider uses. The second library is the Glider library.
Listing 9 shows an example of the glider.
Listing 9. index.html
<html><head>
<link rel="stylesheet" href="stylesheets/glider.css" type="text/css">
<script src="javascripts/prototype.js"></script>
<script src="javascripts/effects.js"></script>
<script src="javascripts/glider.js"></script>
</head><body>
<div id="glider"><div class="controls">
<a href="#tab1">Tab 1</a> |
<a href="#tab2">Tab 2</a> |
<a href="#tab3">Tab 3</a> |
<a href="#tab4">Tab 4</a></div>
<div class="scroller"><div class="content">
<div class="section" id="tab1">Tab 1</div>
<div class="section" id="tab2">Tab 2</div>
<div class="section" id="tab3">Tab 3</div>
<div class="section" id="tab4">Tab 4</div>
</div></div></div>
<script>
new Glider( 'glider', { duration:0.5 } );
</script>
</body></html>
|
At the top of the file are the inclusions of the various script libraries. Then,
there is the glider <div>, which contains first a
<div> called controls that has anchor tags
to get to each tab, then another <div> called
scroller that contains the content for each tab. The script at the bottom
creates a Glider object with the ID of the glider <div>
element.
When I open the page in my browser, I see something like Figure 7.
Figure 7. The tabs example done with a glider
When I click another tab, the content literally glides to it.
I've shown just a few of the different types of interface elements that you can build with Ajax, PHP, and the Prototype.js library. Hopefully, you can take some of these ideas and use them in your own Web application. They are certainly easy enough, and the Prototype.js library really makes Ajax a snap. If you have a success story you'd like to share, please join the developerWorks Ajax forum: We would be happy to hear from you.
Learn
- The PHP home page: Visit an invaluable resource for PHP programmers.
-
Prototype library: Explore this JavaScript Framework designed to ease development of dynamic Web applications.
-
Scriptaculous
JavaScript library: Find display helpers and effects to make your Web site fly with this Prototype-based framework.
-
Prototype.js
documentation page: Get additional information on the Prototype JavaScript library with links to the official Prototype blog and many other resources.
-
jQuery: Explore
another JavaScript library that provides similar functionality to Prototype.js..
-
Yahoo! User Interface Library: Check out Yahoo!'s toolkit for Ajax..
- The Glider control: )Put a cool new twist on the tab style of interaction.
-
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.
- The technology
bookstore: Browse for books on these and other technical topics.
Get products and technologies
-
IBM trial software: Build your next development project with trial software available for download directly from developerWorks.
Discuss
- Participate in the discussion forum.
-
XML zone discussion forums: Participate in any of several XML-related discussions.
-
developerWorks XML zone: Share your thoughts: After you read this article, post your comments and thoughts in this forum. The XML zone editors moderate the forum and welcome your input.
-
developerWorks blogs: Check out these blogs and get involved in the developerWorks community.
-
Ajax discussion forum: Participate in the Ajax forum, moderated by Jack Herrington.
Jack D. Herrington is a senior software engineer with more than 20 years of experience. He's the author of three books: Code Generation in Action, Podcasting Hacks, and PHP Hacks. He has also written more than 30 articles. You can reach Jack at jherr@pobox.com.




