Tuesday, May 10, 2011

jQuery UI URL Tabs

The jQuery UI toolkit provides us with a tabs widget to logically group elements on our page.  This is useful as a top-level navigational tool for most web applications.  The default functionality of this widget allows us to divide content on a single page into tabs.  This is good if every page in your application has a different set of tabs, obviously not well suited for top-level navigation.

We can also configure the jQuery UI tabs widget to load the individual tab content in the background using Ajax requests.  Using this approach, we can reuse the same widget on several pages.  However, maybe we don't want to use asynchronous HTTP requests.  If your application doesn't use it anywhere else, why use it for the top-level navigation?

This also introduces another problem - if you're using the tabs widget as a top-level navigation tool, you probably want the links displayed to the user so they can visit these pages directly.  The tabs demo page actually has a work-around that lets the user follow the link set in the tab.  So it almost behaves like a link, but there is a problem with this approach in that it still appear to be a link to the end user.  For example, they can't right click it and copy link address.  It would be nice if we could use the tabs widget as a top-level navigation widget that uses standard links.  This way we can theme it and attach additional behavior to it if we're so inclined.

Below is an example of how I was able to get this working.  Here is the basic HTML markup.

        <title>jQuery UI URL Tabs</title>
        <link type="text/css" href="jqueryuitheme.css" rel="stylesheet"/>
        <script type="text/javascript" src="jquery.min.js"></script>
        <script type="text/javascript" src="jqueryui.min.js"></script>
        <script type="text/javascript" src="urltabs.js"></script>
        <div id="tabs">
                <li><a href="/page1/">Page 1</a></li>
                <li><a href="/page2/#content">Page 2</a></li>
                <li><a href="/page3/">Page 3</a></li>
            <div id="content">                 
                <p>Some content...</p>

And here is the urltabs.js file used to create the widget.

    //Get the selected tab, and it's URL.
    var selection = $('#tabs ul li a[href$="#content"]');
    var selection_url = selection.attr('href')
                                 .replace('#content', '');
    //Replace the #content href.
    selection.attr('href', '#content');
    //Create the tabs widget, and select the proper tab.
        selected: selection.parent().index(),
    //Make each tab a link by setting the href attribute.
    $('#tabs ul li a').each(function(){
        var url = $.data(this, 'load.tabs');
        if(url) {
            $(this).attr('href', url);
        else {
            $(this).attr('href', selection_url);
    //Make sure the selected tab also behaves like a link.
    $('#tabs ul li.ui-tabs-selected a').click(function(){
        location.href = $(this).attr('href');
        return false;
    }).css('cursor', 'pointer');


And there you have it - a tabs widget that will behave like plain old links.  Now for a few explanations.

The central idea to this approach is that the HTML markup uses a central #content div for displaying the selected tab content.  The selected tab link URL uses this anchor to say "I'm currently selected".  The markup is fairly simple and can easily be handled by any template system and server-side menu constructs.

The construction of the widget itself is done in five steps.  First, we need to locate the currently selected tab and store its URL.  We do this by finding the link with #content at the end.  Next, we replace the URL with #content.  This is required in order to display any content.  Next, we actually create the tabs widget using the specified tab selection.  At this point, we have a tabs widget, but it still wont use regular URL links because the widget will intercept the click events.

There are two things left to do once the widget has been created.  First, we find all tab links and set their URL back to what it was originally, before the widget was created.  However, the selected tab is different - we cannot retrieve the original URL from the widget and so this is why we stored it before the widget was created in selection_url.  Finally, we make the currently selected tab a link too.  Users will intuitively try to click it, so we may as well give them what they want.