- We design and build extraordinary applications for companies looking to make the next great idea a reality.
- learn more
jQuery UI in action: Thumbs up on tabs, thumbs sideways on themes

The first two installments haven't even been published yet, but I spent most of last week hammering out the third entry in the series I'm writing for IBM developerWorks. The first two installments tackled things like modal dialogs, tooltips and lightboxes. But for the latest piece, I delved a little deeper into full-scale Ajax by showing readers how to load page fragments and images on the fly into tabbed interfaces and image carousels. As before, I enjoyed writing for a more beginner/intermediate audience. And I especially enjoyed the chance to play with jQuery and all of its shiny new plug-ins.
Inspired, perhaps, by the success of YUI and Ext, all the big JavaScript frameworks are releasing companion libraries of packaged, themable UI widgets. jQuery is no exception. My latest IBM piece uses jQuery UI Tabs, one of many components in the new jQuery UI framework. UI Tabs began its life as a standalone plug-in. With the launch of jQuery UI, it has become more tightly integrated with the other components. Ultimately, that's the lure of an umbrella concept such as jQuery UI: fewer far-flung extensions of unknown provenance; more plug-and-play efficiency.
That said, jQuery UI is still a work in progress. Themability so far shows more promise than actual results - at least for the one component I used. The process of creating a new theme isn't well documented. Creating a theme pretty much means hacking the default stylesheet and background image that get included with the component. There's still no central place to download themes that can be applied to all of the widgets across the jQuery UI umbrella. That said, the default theme for UI Tabs is quite attractive.
One thing that would be helpful, however: PSD files for the image that's used to generate the backgrounds and rounded corners of the tabs. Despite the fact that it's a PNG, the included graphic doesn't use alpha-channel transparency. If your site's background isn't white, you have to either build your own image from scratch or settle for hacking the single-layer image you've been given. If you go for the latter, the results look OK but not perfect. The included tab images are pretty wide, but things do eventually break if your tab labels are verbose or the font-size gets jacked up high by you or the user.
All of which are quibbles at this stage in the evolution of jQuery UI. The actual code is a breeze to use. Below I've appended sample code adapted from what I wrote for the IBM piece. In it, I turn an unordered list into a series of tabs and pull in the tab content from a mix of hidden divs and separate HTML fragments.
I do so using progressive enhancement. In JS-disabled browsers, the tabs turn into a simple navigational menu and the tabbed content turns into a series of separate HTML pages. As with my entire IBM series, the goal here was to take a non-Ajax site and transform it into an Ajax one that degrades back to its former self the minute you turn JavaScipt off. Doing so with jQuery and jQuery UI is practically painless, requiring only a little DOM traversal; a set of default CSS styles; another set of styles hidden away in a <noscript> tag for the non-JS version; and a templating engine that can serve up the same content as both a full HTML page and an HTML fragment. (In my example code, I cheat, using separate static documents for the two views.)
That brings up one other slight weakness with jQuery UI Tabs: To pull tab content in via Ajax, you use a standard link tag's HREF attribute to store the location of an HTML fragment. Ideally, in the world of progressive enhancement, you'd want to use the HREF attribute to store the URL of a full HTML document for non-JavaScript-capable browsers. The alternative link to an HTML fragment, which would be used for Ajax-capable browsers, would be stored elsewhere, perhaps in an expando property of the same link tag.
You'll see in my code below that I got around this problem by pre-parsing the link elements and altering their URLs before turning them into tabs. jQury UI Tabs certainly doesn't hinder progressive enhancement; it simply makes you jump through a couple of extra hoops to accomplish it.
At any rate, to see my code and jQuery UI Tabs in action, visit the example application on my personal server.
<!--jquery assets-->
<script type="text/javascript" src="../js/jquery-1.2.3.min.js"></script><!--jquery.ui.tabs assets-->
<script type="text/javascript" src="../ui.tabs/ui.tabs.pack.js"></script>
<link rel="stylesheet" href="../ui.tabs/ui.tabs.css" type="text/css" media="print, projection, screen"><script type="text/javascript">
$(document).ready(function() {/*progressive enhancement:
update tab urls to use html fragments instead of
full pages so app degrades gracefully.
first tab is included in the html.
tabs 2-4 are pulled in via Ajax.
*/
$('ul.nav > li:not(:first):not(:last) > a').each(function (i) {
var el = $(this);
el.attr("href", el.attr("href").replace(".html", "-fragment.html"));
});/*more progressive enhancement:
last tab gets special handling because it's got its own
ajax component inside: an image carousel using jCarousel
*/
$('ul.nav > li:last > a').attr("href", "#productImages");/*
SNIP: setup of jCarousel image carousel in the last tab
*//*still more progressive enhancement:
switch the classname on the <ul> we are using to
build our tabs so that the right styles get applied
in JS-enabled and noscript environments
*/
$('ul.nav').attr({"class":"navTabs"});/*create tabs from an unordered list using jquery.ui.tabs*/
$('ul.navTabs').tabs(
{ fx: { height: 'toggle', opacity: 'toggle' } }
);});
</script>
<style type="text/css">
#CMN .tabContent {
padding: 14px;
border: 1px solid #97a5b0;
}
#CMN .tabContent h2{
display: none;
}
#CMN ul.nav {
display: none;
}
</style><noscript>
<style type="text/css">
#CMN .tabContent {
padding: 0;
border: 0;
}
#CMN .tabContent h2 {
display: block;
}
#CMN ul.nav {
display: block;
}
</style>
</noscript>
<h1>Product Details</h1>
<ul class="nav">
<li><a href="#introduction"><span>Introduction</span></a></li>
<li><a href="detailB2.html"><span>More Details</span></a></li>
<li><a href="detailB3.html"><span>User Reviews</span></a></li>
<li><a href="detailB4.html"><span>Technical Specifications</span></a></li>
<li class="last"><a href="detailB5a.html"><span>Product Images</span></a></li>
</ul><div class="tabContent" id="introduction">
<h2>Introduction</h2>
<!--SNIP: a bunch of HTML content-->
</div>
<div class="tabContent" id="productImages">
<h2>Product Images</h2>
<!--SNIP: a bunch of HTML content-->
</div>
Topics: Ajax Development, jQuery, Progressive Enhancement
Comments: 1 so far
Leave a comment
About Pathfinder
Recent
- Rails ThreatDown!
- Automated Deployments Rock
- Bandwidth profiling Flex projects and more with Charles
- iPhone SDK: UIViewController Testing & TDD
- Icons are evil; so are menus - unless you do them right
- The Truth About Designing For Security
- GWT, Gadgets and OpenSocial, Part 2
- Has Many has_many: A Refactoring Story
- The Hidden Power of Canvas
- Review of fixture_replacement2 plugin
Archives
- November 2008
- October 2008
- September 2008
- August 2008
- July 2008
- June 2008
- May 2008
- April 2008
- March 2008
- February 2008
- January 2008
- December 2007
- November 2007
- October 2007
- September 2007
- August 2007
- July 2007
- June 2007
- May 2007
- April 2007
- March 2007
- February 2007
- January 2007
- December 2006
- November 2006
- October 2006
- September 2006
- August 2006
- July 2006
- June 2006
- May 2006
- April 2006
- March 2006


Thanks for the thumbs up!
Although I disagree about what you said about the weakness of the plugin. In fact I think it is its strongness (one wouldn’t expect anything else from the author of the plugin I guess). See, I always strived for making the plugin as unobtrusive and easy to use as possible. Cluttering HTML with a most probably invalid attribute or maybe abusing the rel attribute and having to maintain two different URLs is not my definition of unobtrusiveness and ease of use. Right now you just have to plug in a few links with some clean, gracefully degrading HTML and you’re done. You don’t even have to think about wether you create in-page or remote tabs.
A decent templating engine could decide for itself wether to deliver a page with full layout or just the content part depending on the type of request. In Rails for example this is as easy as adding the following line to the controller actions where needed:
# do not render full layout if page
# is requested via XmlHttpRequest
render :layout => !request.xhr?
This can be done globally as well (DRY).
That said, I don’t think this should be in the responsibility of the plugin.
Anyways, thanks again for using the plugin and happy coding
Comment by Klaus Hartl, Wednesday, February 20, 2008 @ 3:29 am