Nested Tab Sets with JQuery and the Tabs Plugin

OK, I'm working on a small side project with a three column layout. In one column I want to place two multi-step forms. So I decide it would be good to use nested tab sets. From a usability perspective it makes sense, with so many operating system and program dialogs functioning the same way. From a design standpoint it makes sense, because it takes up little space and groups similar content.

I know this can be done, but I've never written it into a web application before, so I immediately start to look at the JQuery plugin libraries. For the uninitiated, JQuery is an extremely well done, and easy to use, JavaScript library for DOM introspection/manipulation, creating event handlers, and a wide variety of other interesting stuff. It has a growing plugin extension library, and is gaining massive support from the web community.

My trip into the Plugin repository quickly turns up what I'm looking for, Tabs by Klaus Hartl. Here's the basic layout: First, place the script to include the JQuery core in the head of your document.

<script type="text/javascript" src="path/to/jquery.js"></script>

Then include the script for the tabs plugin.

<script type="text/javascript" src="path/to/jquery.tabs.pack.js"></script>

Add references to the necessary (and you will rewrite some) stylesheets and styles.

<link rel="stylesheet" href="tabs.css" type="text/css" media="print, projection, screen" />
<!-- Additional IE/Win specific style sheet (Conditional Comments) -->
<!--[if lte IE 7]>
<link rel="stylesheet" href="tabs_ie.css" type="text/css" media="projection, screen" />
<![endif]-->

<script type="text/javascript">
   //<![CDATA[ // Tabs - hide tabs before initialization to avoid flash of content // Add styles via JavaScript for graceful degradation... document.write('<style type="text/css" media="projection, screen">.fragment { display: none; }</style>');
//]]> </script>

Then, add your container elements in your body area.

<div id="container">
<ul class="anchors">
<li><a href="#section-1" tabindex="1">Section 1</a></li>
<li><a href="#section-2" tabindex="1">Section 2</a></li>
</ul>
<div id="section-1" class="fragment">
...
</div>
<div id="section-2" class="fragment">
...
</div>
</div>

The last thing you have to do is tell your script that your container is to be a tabbed area.

<script type="text/javascript">
   //<![CDATA[    $('#container').tabs({fxAutoHeight: true});
   //]]> </script>

And voila! One tab set. But wait, I needed a nested tab set. OK, no problem.

<!-- In the header -->
<script type="text/javascript">
   //<![CDATA[    $('#container').tabs({fxAutoHeight: true});
   $('#section-1').tabs({fxAutoHeight: true});
   $('#section-2').tabs({fxAutoHeight: true});
   //]]> </script>
<!-- Then additions to your container elems -->
<div id="container">
<ul class="anchors">
<li><a href="#section-1" tabindex="1">Section 1</a></li>
<li><a href="#section-2" tabindex="2">Section 2</a></li>
</ul>
<div id="section-1" class="fragment">
<ul class="anchors">
         <li><a href="#form1_step1">Step 1</a></li>
         <li><a href="#form1_step2">Step 2</a></li>
         <li><a href="#form1_step3">Step 2</a></li>
      </ul>
      <div id="form1_step1" class="fragment">
         ...
      </div>
      <div id="form1_step2" class="fragment">
         ...
      </div>
      <div id="form1_step3" class="fragment">
         ...
      </div>
</div>
<div id="section-2" class="fragment">
<ul class="anchors">
         <li><a href="#form2_step1">Step 1</a></li>
         <li><a href="#form2_step2">Step 2</a></li>
         <li><a href="#form2_step3">Step 2</a></li>
      </ul>
      <div id="form2_step1" class="fragment">
         ...
      </div>
      <div id="form2_step2" class="fragment">
         ...
      </div>
      <div id="form2_step3" class="fragment">
         ...
      </div>
</div>
</div>

Wow, I love this stuff! So far so good. And so easy! Now we put in our form elements and form tags. I'll show one simple form for brevity. (NOTE: This doesn't work. You must read on...)

<form name="form1" id="form1" action="process.cfm" method="post" enctype="multipart/form-data">
<div id="section-1" class="fragment">
<ul class="anchors">
      <li><a href="#form1_step1">Step 1</a></li>
      <li><a href="#form1_step2">Step 2</a></li>
      <li><a href="#form1_step3">Step 2</a></li>
   </ul>
   <div id="form1_step1" class="fragment">
      <input type="text" name="field1" id="field1" />
   </div>
   <div id="form1_step2" class="fragment">
      <input type="text" name="field2" id="field2" />
   </div>
   <div id="form1_step3" class="fragment">
      <input type="text" name="field3" id="field3" /><br />
      <input type="submit" name="submit" id="submit" value="Post Form" />
   </div>
</div>
</form>

Structurally this makes sense. Since our form is spread across the multiple tags we need the opening and closing form tags around the entire form. The problem here is our tab set now breaks, the display of our .fragment div containers no longer work. So, I went back and forth one morning/afternoon, in the comments section on Klaus's blog. Klaus was very helpful, and once he understood what I was trying to do he said "Why don't you use the Form as your container?" Wow, thirteen years writing HTML and I never thought of the form tags as a container set. So, I gave it a go.

<form name="section-1" id="section-1" class="fragment" action="process.cfm" method="post" enctype="multipart/form-data">
<ul class="anchors">
      <li><a href="#form1_step1">Step 1</a></li>
      <li><a href="#form1_step2">Step 2</a></li>
      <li><a href="#form1_step3">Step 2</a></li>
   </ul>
   <div id="form1_step1" class="fragment">
      <input type="text" name="field1" id="field1" />
   </div>
   <div id="form1_step2" class="fragment">
      <input type="text" name="field2" id="field2" />
   </div>
   <div id="form1_step3" class="fragment">
      <input type="text" name="field3" id="field3" /><br />
      <input type="submit" name="submit" id="submit" value="Post Form" />
   </div>
</form>

<!-- This also requires changes to your tab set calls -->
$('#section-1').tabs({fxAutoHeight: true, tabStruct: 'form'});

Bam! Nested tab sets with forms. A big Thank You to Klaus for helping me work that one out. This JQuery stuff is bang on, and the community is very active. I even used the Rounded Corners plugin to apply rounded corners to tops of the tags (Though it only works in Firefox). Huge news, as well, is that Jack Slocum is porting over his Yahoo User Interface extension library to a full JQuery implementation. Great stuff.

Related Blog Entries

Comments
Jim Priest's Gravatar jQuery rocks! It seems to really be taking off in the ColdFusion community. Now where's the demo of your tabs? :)
# Posted By Jim Priest | 4/24/07 8:17 AM
wheezer's Gravatar Uh.. I desperately need my tabs to nest. I am a bit troubled that for all this wonderful write up, there's no demo. I guess I have to build it to to prove it, eh?
# Posted By wheezer | 6/15/07 12:39 AM
Cutter's Gravatar @Jim and @wheezer

I'll see if I can't whip up a live demo with a code download for my next post. I might even to two posts, one with the JQuery Plugin and one with the ExtJS component.
# Posted By Cutter | 6/15/07 8:00 AM
wheezer's Gravatar Great. I was just about to paste all your code into a project. Before I do, can you give me a hint about something?

I need not just nested tabsets on a page, but alas, also more than one set on a page. Will this be doable with your solution, do you think?
# Posted By wheezer | 6/15/07 8:09 AM
wheezer's Gravatar Hi again
I cannot even get your example here to run. Perhaps he's updated his code? I eagerly await your demo. Kinda stuck without it. If you can even show something that just loads, I can fool with it, but right now. I cannot even get that. It might be less confusing if y ou just had one example (with nesting), and one complete header, I am not sure what goes with what, with the way it's presented above.
# Posted By wheezer | 6/15/07 10:03 AM
Thomas's Gravatar A form replaces my normal tab container! Whoo hoo! Who would have thought?
- Thomas
# Posted By Thomas | 10/5/07 2:51 PM
BlogCFC v. 5.8.001 was created by Raymond Camden. Layout inspired by bluerobot.com., with some JQuery thrown in for fun.