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.

view plain print about
1<script type="text/javascript" src="path/to/jquery.js"></script>

Then include the script for the tabs plugin.

view plain print about
1<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.

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

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

12</script>

Then, add your container elements in your body area.

view plain print about
1<div id="container">
2 <ul class="anchors">
3 <li><a href="#section-1" tabindex="1">Section 1</a></li>
4 <li><a href="#section-2" tabindex="1">Section 2</a></li>
5 </ul>
6 <div id="section-1" class="fragment">
7 ...
8 </div>
9 <div id="section-2" class="fragment">
10 ...
11 </div>
12</div>

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

view plain print about
1<script type="text/javascript">
2    //<![CDATA[
3    $('#container').tabs({fxAutoHeight: true});
4    //]]>

5</script>

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

view plain print about
1<!-- In the header -->
2<script type="text/javascript">
3    //<![CDATA[
4    $('#container').tabs({fxAutoHeight: true});
5    $('#section-1').tabs({fxAutoHeight: true});
6    $('#section-2').tabs({fxAutoHeight: true});
7    //]]>

8</script>
9<!-- Then additions to your container elems -->
10<div id="container">
11 <ul class="anchors">
12 <li><a href="#section-1" tabindex="1">Section 1</a></li>
13 <li><a href="#section-2" tabindex="2">Section 2</a></li>
14 </ul>
15 <div id="section-1" class="fragment">
16 <ul class="anchors">
17            <li><a href="#form1_step1">Step 1</a></li>
18            <li><a href="#form1_step2">Step 2</a></li>
19            <li><a href="#form1_step3">Step 2</a></li>
20        </ul>
21        <div id="form1_step1" class="fragment">
22            ...
23        </div>
24        <div id="form1_step2" class="fragment">
25            ...
26        </div>
27        <div id="form1_step3" class="fragment">
28            ...
29        </div>
30 </div>
31 <div id="section-2" class="fragment">
32 <ul class="anchors">
33            <li><a href="#form2_step1">Step 1</a></li>
34            <li><a href="#form2_step2">Step 2</a></li>
35            <li><a href="#form2_step3">Step 2</a></li>
36        </ul>
37        <div id="form2_step1" class="fragment">
38            ...
39        </div>
40        <div id="form2_step2" class="fragment">
41            ...
42        </div>
43        <div id="form2_step3" class="fragment">
44            ...
45        </div>
46 </div>
47</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...)

view plain print about
1<form name="form1" id="form1" action="process.cfm" method="post" enctype="multipart/form-data">
2<div id="section-1" class="fragment">
3 <ul class="anchors">
4        <li><a href="#form1_step1">Step 1</a></li>
5        <li><a href="#form1_step2">Step 2</a></li>
6        <li><a href="#form1_step3">Step 2</a></li>
7    </ul>
8    <div id="form1_step1" class="fragment">
9        <input type="text" name="field1" id="field1" />
10    </div>
11    <div id="form1_step2" class="fragment">
12        <input type="text" name="field2" id="field2" />
13    </div>
14    <div id="form1_step3" class="fragment">
15        <input type="text" name="field3" id="field3" /><br />
16        <input type="submit" name="submit" id="submit" value="Post Form" />
17    </div>
18</div>
19</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.

view plain print about
1<form name="section-1" id="section-1" class="fragment" action="process.cfm" method="post" enctype="multipart/form-data">
2 <ul class="anchors">
3        <li><a href="#form1_step1">Step 1</a></li>
4        <li><a href="#form1_step2">Step 2</a></li>
5        <li><a href="#form1_step3">Step 2</a></li>
6    </ul>
7    <div id="form1_step1" class="fragment">
8        <input type="text" name="field1" id="field1" />
9    </div>
10    <div id="form1_step2" class="fragment">
11        <input type="text" name="field2" id="field2" />
12    </div>
13    <div id="form1_step3" class="fragment">
14        <input type="text" name="field3" id="field3" /><br />
15        <input type="submit" name="submit" id="submit" value="Post Form" />
16    </div>
17</form>
18
19<!-- This also requires changes to your tab set calls -->
20$('#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.