Ajax techniques have changed the face of large, commercial Web applications, but many smaller Web sites don't have the resources to rebuild their entire user interfaces (UIs) overnight. New features should justify their cost by solving real-world interface problems and improving the user experience. This article shows how you can eliminate pop-up windows and navigational dead-ends with simple modal windows. By using the principle of progressive enhancement, you can guarantee that such advanced UI features don't hamper your site's accessibility and adherence to Web standards.
This article assumes you have a solid grasp of Hypertext Markup Language (HTML) and Cascading Style Sheet (CSS) and at least a basic understanding of JavaScript programming and Ajax. The sample application is built using only client-side code; the techniques demonstrated could be adapted to any server-side application framework. To run the example site, you need at least a basic Web server running on localhost. Alternatively, you can just follow along in the source code and see the example site in action on my Web server (see Resources for a link).
Introducing the concept: Retrofitting your site with Ajax
The need to guide your users down a specific path—say, from product search to checkout—is as old as the Web itself. Yet it's fraught with danger: the danger of losing your users along the way. The longer and more complex your navigational path, the more places for users to bail. You need to present those users with enough information to keep them interested in the process at hand.
In the Web 1.0 world, shopping sites streamlined their user experience by constructing a straight path from search and results through selection and purchase. Whenever users wanted more information than the purchase path could provide, they had to take a navigational detour to an information-rich product details or comparison page. The problem with these "side streets" is that they take users out of the purchase path and give them more chances to abandon the process. They're also difficult to maintain because your navigation logic has to store information about how a user got to this cul-de-sac.
Pop-up windows seemed to offer a solution. With supplemental information relegated to pop-ups, the straight, step-by-step path could continue uninterrupted in the main window. Unfortunately, however, pop-up windows are confusing and annoying. They may be easier to maintain than navigational side streets, but they're just as likely to take your users out of the process you want them to complete.
Luckily, open source JavaScript libraries offer an easy way to get rid of side streets and pop-ups once and for all. This article demonstrates how to use Ajax and Dynamic HTML (DHTML) techniques to render your supplemental information inside tooltips, lightboxes, and other modal windows. Because these elements can be inserted on-the-fly into any page, they help preserve the quick, step-by-step path from home page to purchase.
Introducing the application: Customize Me Now
The example application in this article focuses on e-commerce. I've constructed an imaginary shopping application called Customize Me Now that allows users to personalize and purchase an array of disparate products: a pizza, a vacation package, or an investment portfolio. In real life, of course, these product categories would never live on the same site. But together, they demonstrate the kind of complex, real-world navigational challenges many sites face.
By presenting a Web 1.0 version of Customize Me Now, and then retrofitting it into a Web 2.0 version, you can see just how streamlined your navigational funnel can become when you know that supplemental information is only a single Ajax call away. The techniques covered here can be adapted to any process where the need for simplicity competes with the need to educate users. Configuring a product, taking a survey, signing up for a bundle of services, or simply completing a registration form—all these processes can be streamlined with Ajax.
Introducing the techniques: Ajax, tooltips, modal windows, and lightboxes
By now, Ajax probably needs no introduction: It has become so ubiquitous in the
Web-development world that it has lost its all-caps status. For years now, savvy
programmers have been using JavaScript code to update Web pages incrementally,
without a round-trip to the server. But it was only with the adoption of the
xmlHttpRequest object—originally a Windows®
Internet Explorer® extension but now supported across a wide variety of
browsers—that Ajax went mainstream. Anytime you use a Web application that
feels more like a desktop application, chances are you're seeing Ajax in action.
This article doesn't go into the basics of Ajax programming, but you will be using
many open source libraries that use Ajax techniques.
Ajax UIs often employ tooltips, lightboxes, and modal windows. These are all just fancy names for pop-up screens that appear within a browser viewport rather than being launched in a separate window. Tooltips are typically small windows that provide contextual content, usually when the user hovers over a trigger element. Modal windows are typically larger and usually triggered by a click event. Lightboxes are a specific kind of modal window in which a translucent overlay separates the window's original content from the modal content. Any of these containers can be filled with a variety of content: inline content that's been hidden with DHTML techniques; new content pulled from the server by an Ajax call; or entirely separate documents pulled into an iframe. Netflix, the popular DVD rental service, provides a compelling example of these interface elements in action.
Introducing the tools: jQuery, GreyBox, ThickBox, JTip, and jQuery forms
Open source JavaScript toolkits have multiplied since Ajax went mainstream in 2005. Each has its strengths, weaknesses, and quirks, but the best ones paper over browser differences and provide an elegant, cross-browser application program interface (API) for Ajax, DHTML, and visual effects. Some solutions, such as the Google Web Toolkit, use server-side Java™ code to generate client-side JavaScript code automatically. Most, however, are native JavaScript libraries.
You're going to work with one such library, jQuery, whose elegant API and philosophy of unobtrusive JavaScript have earned it an enthusiastic following since its 2006 debut. Using jQuery, you can take an existing, Ajax-free Web 1.0 Web site and turn it into an Ajax-powered Web 2.0 site with almost no changes to the server code, the HTML markup, or the CSS. Your JavaScript code will transform existing HTML elements and behaviors at run time within the browser. If JavaScript is disabled or the browser doesn't support it, the markup will still work the way it always did. This progressive enhancement principle ensures that your Web applications remain accessible to the broadest possible range of users. Mobile devices, assistive software for the physically impaired, or even decade-old Web browsers: All will still be able to run your application.
In addition to its support for progressive enhancement, jQuery allows you to leverage a large, vibrant community of plug-in developers. Many JavaScript toolkits attempt to anticipate every possible developer need, but jQuery focuses on the basics. The core library remains light, while additional capabilities are delivered as plug-ins by the open source community. Specifically, you'll employ the following plug-ins:
- ThickBox: Developed by Cody Lindley
- jQuery Forms: A collaborative effort of the jQuery community
- JTip: Developed by Cody Lindley and extended by the jQuery community
- GreyBox: Developed by Amir Salihefendic
Understanding the application: Customize Me Now 1.0
The following sections help you familiarize yourself with Customize Me Now 1.0 — its user experience and its source code.
Figure 1 shows the Customize Me Now Results page and an informational pop-up window. It demonstrates how much information the site offers users about the products they're customizing. Although the optimal user path is a straight line from search and results to customization and purchase, the interface offers several detours from this route. The site uses traditional pop-up windows to present small chunks of contextual information about each product and product option. Click Pizza, and you see information about the site's pizza product. Click Cheese, and you see information about the available cheeses for your pizza. In addition, users can navigate to the manufacturer's Web site—also rendered in a pop-up window.
Figure 1. The Results page 1.0
If users want to get even more details, the site offers a Product Details page with far more information about each product -- pictures, articles, user reviews, and so on. Finally, if users want to see how individual products stack up against one another, they can view them side-by-side on the Comparison page. Navigation among all these resources is complex. Because users need to customize each product, it's hard simply to offer an Add to cart link at each step of the way. The site encourages users to customize, then add to cart. But it also offers the ability to flip those steps by adding to cart with a set of default options, then customizing later.
Figure 2 provides a wireframe site map of Customize Me Now 1.0. It demonstrates just how convoluted a user's path can become. Most screens link to at least two or three other screens.
Figure 2. Customize Me Now 1.0 site map
The functional demo code for Customize Me Now provides just the client-side assets: HTML CSS, JavaScript code, and image files. In the real world, you would obviously have a large server-side component, too: Microsoft® ASP.NET, Java technology, Ruby on Rails, Django, or PHP. But the beauty of jQuery is that it's completely client-side. You can simplify your user experience using Ajax techniques without having to mess with your server components. I haven't provided any server-side code because those details don't matter for the project at hand. Just know that whenever you open an HTML file, it could easily be a PHP template, a JavaServer Pages™ (JSP) file, or any other file that sends HTML to the browser.
After you've downloaded the source code for the version 1.0 application, look at the markup for one of your pages. It should look like Listing 1.
Listing 1. HTML code for results.html 1.0
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>Customize Me Now: Search Results</title>
<link rel="stylesheet" type="text/css" href="../css/customizemenow.css"/>
</head>
<body id="CMN">
<!--[header goes here]-->
<div id="main">
<form method="GET" action="comparison.html">
<h1>Search Results</h1>
<div class="buttons">
<input class="button" type="submit" name="submitTop" id="submitTop"
value="Compare Selected Products" />
or <a href="index.html">search again</a>
</div>
<table class="searchResults">
<thead>
<tr>
<th>Product</th>
<th>Description</th>
<th>Options</th>
<th>Compare</th>
<th>Actions</th>
</tr>
</thead>
<tr>
<td class="name">
<a target="productPopup"
href="productPopup.html?product=A">Pizza</a>
</td>
<td class="desc">
A delicious Italian treat.<br />
[<a href="detail.html?product=A">full product details</a>]
</td>
<td class="options">
<ul>
<li>
<a target="optionsPopup"
href="optionsPopup.html?product=A">crust</a>
</li>
<li>
<a target="optionsPopup"
href="optionsPopup.html?product=A">cheese</a>
</li>
<li>
<a target="optionsPopup"
href="optionsPopup.html?product=A">toppings</a>
</li>
</ul>
</td>
<td class="action">
<input type="checkbox" target="productPopup" name="compareA"
id="compareA" value="true" checked="checked"/>
</td>
<td class="action">
<a class="button" href="customize.html?product=A">customize
product</a>
<a class="button" href="cart.html?product=A">add to cart with
default options</a>
</td>
</tr>
<!--[additional table rows go here]-->
</table>
<div>
<div class="buttons">
<input class="button" type="submit" name="submitBottom" id="submitBottom"
value="Compare Selected Products" />
or <a href="index.html">search again</a>
</div>
</form>
</div>
<!--[footer goes here]-->
</body>
</html>
|
The process of retrofitting Customize Me Now into its version 2.0 incarnation is described first in this installment and is further developed in Part 2 of the series.
Install jQuery and its plug-ins
The first step to adding an Ajax behavior layer to your site is to download all of your open source libraries. If you downloaded the sample 2.0 application from the Download section, all the libraries are included. If you want to download the latest versions of these libraries directly for your own application, you can once again do so in the Downloads section.
Next, create a /js directory for jQuery and the forms plug-in. Note that GreyBox, ThickBox, and JTip all need their own directories; they come bundled with images, CSS files, and multiple JavaScript libraries, all of which assume a particular directory structure. When linking to your CSS and .js files, you must also include a short script block to set up the correct root-directory pointer for GreyBox. This pointer must be an absolute directory path, so you may need to tweak this value in your own code. When you're done, the head elements of your HTML files should look like Listing 2.
Listing 2. Customize Me Now 2.0 header element
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>Customize Me Now: Shopping Cart</title>
<!--customizemenow assets-->
<link rel="stylesheet" type="text/css" href="../css/customizemenow.css"/>
<!--jquery assets-->
<script type="text/javascript" src="../js/jquery-1.2.1.min.js"></script>
<script type="text/javascript" src="../js/jquery.form.js"></script>
<!--thickbox assets-->
<script type="text/javascript" src="../thickbox/thickbox.js"></script>
<link rel="stylesheet" type="text/css" href="../thickbox/thickbox.css" />
<!--jtip assets-->
<script type="text/javascript" src="../jtip/scripts/jtip.js"></script>
<link rel="stylesheet" type="text/css" href="../jtip/css/jtip.css" />
<!--greybox assets-->
<script type="text/javascript">
/*this needs to be a non-relative reference*/
var GB_ROOT_DIR = "/customizemenow/2/0/greybox/";
</script>
<script type="text/javascript" src="../greybox/AJS.js"></script>
<script type="text/javascript" src="../greybox/AJS_fx.js"></script>
<script type="text/javascript" src="../greybox/gb_scripts.js"></script>
<link rel="stylesheet" type="text/css" href="../greybox/gb_styles.css" />
</head>
|
Convert secondary links into lightboxes using ThickBox and jQuery forms
Because jQuery and its plug-ins follow a philosophy of progressive enhancement,
you need almost no custom JavaScript code to create your Ajax functionality.
Instead, you add specific attributes to existing HTML tags. Your JavaScript
libraries parse the Document Object Model (DOM) looking for these special
attributes, then add the appropriate behaviors to elements that possess them.
jQuery enables this coding style by parsing the DOM automatically when all the
markup has been rendered. If you peek behind the scenes of your jQuery plug-ins,
you'll see that they all delegate their event models to the core
jQuery object and its ready
method.
Now that you've learned the background, it's time to begin simplifying your navigation with jQuery. You use ThickBox to transform any page that's not part of the main purchase path from a regular page into a lightbox page. You begin with the Product Details page so your users can view it from anywhere on the site without leaving the "funnel."
ThickBox is easy to invoke. Simply add some special attributes to each relevant link:
- A
classattribute ofthickbox. This specialclassattribute signals to ThickBox that it should pay attention to this element. - Several querystring values:
-
KeepThis=trueandTB_iframe=true: These values tell ThickBox to render this link in an iframed lightbox. -
height=400: The height of your ThickBox window in pixels. This could be any value, but for this example, use 400. -
width=600: The width of your ThickBox window in pixels. This could be any value, but for this example, use 600.
-
Because you're already using the querystring to pass a product code to the Product Details page, simply append your ThickBox values to the existing URL using ampersands (&). When you're done adding these attributes to your Product Details links, each pop-up link should look like Listing 3.
Listing 3. HTML code for ThickBox links
<a
href="detail.html?product=A&KeepThis=true&TB_iframe=true&height=400&width=600"
class="thickbox">product details</a>
|
Now that you've tackled the Product Details page, retrofit the Comparison page.
The only way to get to this page is through a form submission; users have to
choose which products they want to compare by using check boxes. You can't
render the results of this form submission using ThickBox without leveraging
jQuery Forms, a library that wraps several convenience methods and event hooks
into an object called ajaxForm. Using
ajaxForm and a bit of custom JavaScript code, you
can call ThickBox's tb_show method directly. To
do so, add the script block in Listing 4 to the head of
results.html.
Listing 4. JavaScript code to invoke ThickBox from a form
<script type="text/javascript">
/*create a thickbox for our form submittal*/
//when the document is ready
$(document).ready(function() {
//wrap form#comparison in an ajaxForm object
$('#comparison').ajaxForm({
//intercept the submit event with a callback
beforeSubmit: function(formData, jqForm, options) {
//serialize form data; append to the form action; tack on ThickBox params
var URL = jqForm[0].action + "?" + $.param(formData);
URL += "&KeepThis=true&TB_iframe=true&height=400&width=600";
//call ThickBox directly
var caption = null;
var imageGroup = false
tb_show(caption,URL,imageGroup);
//cancel the form submission by returning false
return false;
}
});
});
</script>
|
This code block illustrates the simplicity of the jQuery API. In just a few lines of code, you're intercepting the normal submittal of an HTML form and executing some custom JavaScript code instead. Using this technique, you could execute custom validation logic before allowing the form to be submitted, or you could trigger a custom event after the form is submitted. In this case, you're going to keep the form from submitting altogether. Instead, you're going to manually "fake out" the HTTP request that the form submittal would have generated so that you can redirect the target of the form to your ThickBox window.
Users are unaware of all this behind-the-scenes action. All they know is that after submitting the form, they see the results in their modal window. After looking at your Comparison page, the user can close the ThickBox window and get back to customizing and purchasing a product.
The only problem with rendering your Product Details and Comparison pages using
ThickBox is that the pages are too wide for your ThickBox window. You could
change the width and height values that you pass in to ThickBox, but what about
users with smaller viewports? You don't want ThickBox to cover the entire
window, let alone extend beyond the edges of the viewport. Instead, simply
re-style details.html and comparison.html by adding a
class of inline to their
body tags. Then, add the CSS declaration from Listing 5
to customizemenow.css.
Listing 5. CSS for ThickBox
#CMN #main.inline {
width: 600px;
}
|
The final step in your modal window implementation is to suppress those elements of the Product Details and Comparison pages with which you don't want users to interact. Because these pages are now purely informational, there's no need to include action links and buttons within them. There's also no need to show your navbars or other chrome.
You could accomplish this in several ways. You could simply remove these elements
from the pages, but that would defeat your strategy of progressive enhancement.
Users who don't enable JavaScript would end up on these pages with no way to get
off them or advance through the purchase process. You could also append
querystring parameters to your links so that your server-side framework would
render these pages using a different template. In the real world, that's what
you'd probably do. But there's another valid way to handle this, one that
relies on only client-side code: the good old-fashioned
<noscript> tag. If you wrap each element in a
<noscript> tag, it will be seen only by users
of non-JavaScript user agents: the exact users you want to see them.
The resulting HTML code for your headers and footers looks like the code in Listing 6.
Listing 6. HTML code for Customize Me Now 2.0 navigation
<noscript>
<div id="footer" class="nav">
<<ul>
<li><a href="index.html">search&/a></li>
<li><a href="results.html">results</a></li>
<li><a href="detail.html">details</a></li>
<li><a href="comparison.html">compare</a></li>
<li><a href="customize.html">customize</a></li>
<li><a href="cart.html">cart</a></li>
<li><a href="checkout.html">checkout</a></li>
<li class="last"><a href="confirm.html">confirmation</a></li>
</ul>
</div>
</noscript>
|
The HTML for the main content <div> of your
Product Details page looks like the code in Listing 7.
Listing 7. HTML code for details.html 2.0
<div id="main" class="inline">
<form method="GET" action="customize.html">
<input type="hidden" name="product" id="product" value="A" />
<h1>Pizza: Product Details</h1>
<noscript>
<div class="buttons">
<input class="button" type="submit" name="submitTop" id="submitTop"
value="Customize Now" />
or <a href="cart.html?product=A">add to cart with default
options</a>
</div>
</noscript>
<!--[content goes here]-->
<noscript>
<div class="buttons">
<input class="button" type="submit" name="submitBottom" id="submitBottom"
value="Customize Now" />
or <a href="cart.html">add to cart with default options</a>
</div>
</noscript>
</form>
</div>
|
The HTML for the main content <div> of your
Comparison page looks like the code in Listing 8.
Listing 8. HTML code for comparison.html 2.0
<div id="main" class="inline">
<h1>Product Comparison</h1>
<table class="productComparison">
<thead>
<tr>
<th>Product</th>
<th>Pros</th>
<th>Cons</th>
<noscript>
<th>Actions</th>
</noscript>
</tr>
</thead>
<tr>
<td class="name">
<a class="jTip" name="About Pizza" id="pizza" target="productPopup"
href="productPopup.html?product=A">Pizza</a>
</td>
<td class="pros">
<ul>
<li>Great flavor.</li>
<li>Low cost.</li>
<li>Fun with friends.</li>
</ul>
</td>
<td class="cons">
<ul>
<li>Can make you fat.</li>
<li>Not very nutritious.</li>
</ul>
</td>
<noscript>
<td class="action">
<a class="button" href="customize.html?product=A">customize
product</a>
<a class="button" href="cart.html?product=A">add to cart with
default options</a>
</td>
</noscript>
</tr>
<!--[additional table rows here]-->
</table>
</div>
|
You can see the results of all this tinkering by viewing the Customize Me Now 2.0 Search Results page in a browser and launching the Product Details or Comparison pages. It should look like the one in Figure 3.
Figure 3. ThickBox in action
While this article covered a great deal of material — showing you some Ajax techniques and best practices -- we're just getting started. In Part 2 of this series, you will continue to refine your navigation by transforming pop-up links into tooltips using JTip. Then, you'll convert off-site links into lightboxes using GreyBox. Finally, you'll review all the key concepts behind the example application and examine how they've improved its user experience. If you want to work ahead, you can dig deeper into the source code for Customize Me Now 2.0 and look at it in action in a Web browser.
| Description | Name | Size | Download method |
|---|---|---|---|
| Source code for the original demo app | customizeOnePointZero.zip | 24KB | HTTP |
| Source code for the retrofitted demo app | customizeTwoPointZero.zip | 88KB | HTTP |
Information about download methods
More downloads
- Code sample: jQuery1
- Code sample: JTip2
- Code sample: ThickBox3
- Code sample: jQuery Forms4
- Code sample: GreyBox5
Notes
- This open-source Ajax toolkit provides the foundation for your Ajax capabilities. The current version as of this writing is 1.2.1.
- This jQuery plug-in allow you to replace your informational pop-ups with simple, cross-browser tooltips.
- This plug-in allows you to load your product details and comparison pages inside a modal window. The example in this article uses version 3.1.
- Because the comparison page requires some form parameters, you must write a bit of custom JavaScript code to render it with ThickBox. Luckily, this utility library does most of the work for you.
- This jQuery plug-in lets you link to your manufacturer Web sites in a simple, gorgeous lightbox. The version in this example is 5.53.
Learn
-
See Customize Me Now 1.0
in action on my Web site.
-
View Customize Me Now 2.0,
with all of the changes, in action on my Web site.
-
Go to the jQuery Web site to learn all about jQuery and
find additional plug-ins.
- Visit the Learning jQuery Web site to participate in the
jQuery community and access tutorials and forums.
-
The developerWorks Web development zone is packed with tools and information for Web 2.0 development.
-
The developerWorks Ajax resource center contains a growing library of Ajax content as well as useful resources to get you started developing Ajax applications today.
- Read
"Simplify
Ajax development with jQuery" (Jesse Skinner, developerWorks, 10 Apr 2007) to learn about the jQuery philosophy, discover its features and functions, perform some common Ajax tasks, and find out how to extend jQuery with plug-ins.
- "Ajax and XML:
Ajax for lightboxes" (Jack Herrington, developerWorks, 25 Sep 2007) teaches you how
to implement lightboxes and tooltips with the Prototype JavaScript library.
-
Read
Learning
jQuery
to get started with jQuery.
-
Check out the book
jQuery
in Action
for additional jQuery help.
- Turn to the
jQuery
Reference Guide
for a more general jQuery resource.
-
Check out Brian Dillard's blog, Agile Ajax,
for more on jQuery and other UI topics.
-
Stay current with developerWorks
technical events and Webcasts.
-
Browse the technology
bookstore for books on these and other technical topics.
Discuss
-
Get involved in the developerWorks community. Participate in
developerWorks blogs.

In his 12 years as a Web developer, Brian J. Dillard has built rich user interfaces for companies as diverse as Orbitz Worldwide, Reflect True Custom Beauty, Archipelago LLC, and United Airlines. Now serving as RIA Evangelist at Pathfinder Development in Chicago, Illinois, Brian builds rich Internet applications for a variety of clients, participates in open source projects, and contributes to the Agile Ajax blog. He is the project lead on Really Simple History, an Ajax history and bookmarking library used in the production code of thousands of Web sites.





