Stuart Hilbert was recently asking how you could create tabs inside a module in DotNetNuke 4.9.1. In this post I’ll outline the technique we use on the Dashboard panel and for a module I am working on for DotNetNuke Professional Edition.
When building tabs I like to separate the development into two separate components: the visuals and the behavior. This separation allows me to easily change each aspect without requiring a substantive change to the other component. If I need a vertical navigation structure, I can easily accommodate that without changing my code.
Building the Visuals
Since we are talking about building a flexible visual structure for display on the web we’ll use CSS to handle the look and feel for our tabs. I am not one for re-inventing the wheel so I leaned on a great program for quickly getting up and running. I have found CSS Tab Designer from OverZone Software to be very helpful in getting a nice structure to start from. The software has a lot of different tab styles which use nice, clean semantic HTML with CSS styling. Of course this software is just a shortcut for implementing common CSS techniques for tabs.
My first introduction to CSS tabs was from the Sliding Doors of CSS article on A List Apart. This technique was further expanded in Sliding Doors Meets CSS Sprites which incorporated the popular CSS sprite techniques described by Dave Shea. Not all of the designs in CSS Tab Designer will use Sliding Doors and Sprites, but they all follow well known CSS techniques which are easily implemented since the program provides you with working HTML and CSS. You won’t need to go scouring CSS tutorial sites looking for good examples.
The basis for almost all of the tab designs is a nice simple unordered list. If this were not using the sliding doors technique we wouldn’t need the inner spans around our tab name. We’ll make some minor mods to this structure when we discuss the jQuery behavior that we’ll add later.
<ul id="jquerytablist">
<li class=”active” ><a class="jqueryTab">
<span>DNN Corp</span></a>
</li>
<li><a class="jqueryTab">
<span>DotNetNuke<span></a>
</li>
</ul>
Using this structure we are able to add the following CSS and a few images to get nice looking tabs.
/* Tab Rendering */
#jquerytablist { float:left; width:100%; font-size:93%; line-height:normal; border-bottom:1px solid #24618E; margin:0; padding:10px 10px 0 10px; list-style:none; }
#jquerytablist li { display:inline; margin:0; padding:0; }
#jquerytablist a { float:left; background:url("images/tableft.jpg") no-repeat left top; margin:0; padding:0 0 0 5px; text-decoration:none; }
#jquerytablist a span { float:left; display:block; background:url("images/tabright.jpg") no-repeat right top; padding:5px 15px 4px 6px; color:#24618E; }
/* Commented Backslash Hack hides rule from IE5-Mac \*/
#jquerytablist a span {float:none;}
/* End IE5-Mac hack */
#jquerytablist a:hover span { color:#FFF; }
#jquerytablist a:hover { background-position:0% -42px; }
#jquerytablist a:hover span { background-position:100% -42px; }
#jquerytablist li.active a { background-position:0% -42px; }
#jquerytablist li.active a span { background-position:100% -42px; color:#FFF; }

To show how flexible this design is, we can change the CSS like so:
/* Tab Rendering */
.jquerytablist { list-style:none; width: 200px; border-style: solid solid none solid; border-color: #94AA74; border-width: 1px; padding: 0; margin: 10px; float:left; }
.jquerytablist li { list-style:none; }
.jquerytablist li a { height: 32px; voice-family: "\"}\""; voice-family: inherit; height: 24px; text-decoration: none; }
.jquerytablist li a:link, .jquerytablist li a:visited { color: #5E7830; display: block; background: url(images/menu1.gif); padding: 8px 0 0 10px; }
.jquerytablist li a:hover, .jquerytablist li.active a { color: #26370A; background: url(images/menu1.gif) 0 -32px; padding: 8px 0 0 10px; }
.jquerytablist li a:active { color: #26370A; background: url(images/menu1.gif) 0 -64px; padding: 8px 0 0 10px; }*/
/* Tab Panels */
.tabPanel { margin-left: 210px; padding: 10px; }
To get this vertical menu without changing a single line of HTML or JavaScript:
Building the Behavior
Now that our tabs are looking nice it is time to add some behavior so that they actually work. As many know, I have become enamored of jQuery lately. It makes tasks like this very simple. In the past I would have used postbacks and handled the tab rendering on the server. That solution works, but the postbacks break the illusion and make the interface seem clunky rather than smooth and fluid. In this day and age we have great tools like jQuery which handle problems like this with ease.
Before adding our jQuery, lets add a few tweeks to our HTML. We need to add some panels to display our content, a method to link panels to tabs and a container to hold everything together. Also, I have removed almost use of id’s and shifted to using classes instead. While this has some minor performance implications, it will help to avoid conflicts if you have multiple sets of tabs on the same page.
<div class="jqueryTabs" >
<ul class="jquerytablist">
<li class=”active” ><a href="#first-tab" class="jqueryTab">
<span>DNN Corp.</span></a>
</li>
<li><a href="#second-tab" class="jqueryTab">
<span>DotNetNuke</span></a>
</li>
</ul>
<div id="first-tab" class="tabPanel">
Some Content
</div>
<div id="second-tab" class="tabPanel" style=”display:none”>
More Content
</div>
</div>
Tabs and their associated panels are linked through the href tag and the panel ID. In this case I have retained the panel ID since it is not that hard to keep these IDs unique, even when generated from code, and the jQuery code doesn’t care what the specific ID is, only that the tab and panel use the same name. Now we are ready to hook up the jQuery.
jQuery(document).ready(function() {
jQuery('.jquerytablist > li > a')
.click(function(e) {
jQuery('.jquerytablist > li')
.removeClass('active');
jQuery(this)
.parent()
.addClass('active');
var currentTab = jQuery(this).attr('href');
jQuery('.jqueryTabs div[id$="-tab"]').hide();
jQuery(currentTab).show();
return false;
});
// clean up to avoid memory leaks in certain versions of IE 6
jQuery(window).bind('unload', function() {
jQuery('.jquerytablist > li > a').unbind('click');
});
});
Most of this is pretty straight forward. We add a click event to each tab link. When clicked we remove the active setting from all tabs, make the clicked tab active and show and hide the appropriate panels. One thing to note here is that I avoid the use of the $ alias. This helps to avoid any JavaScript collisions and is a good practice to follow when working in an environment like DNN where you never know what JavaScript may be used by other modules on the page.
The final step is adding the appropriate JavaScript to the the page. I use a simple method for injecting my JavaScript links into the page header.
Private Sub RegisterJQueryScript(ByVal scriptName As String, Optional ByVal IsCore As Boolean = False)
Dim scriptRoot As String = IIf(IsCore, coreScriptRoot, Me.TemplateSourceDirectory).ToString
Dim scriptFormat As String = "<script type=""text/javascript"" src=""{0}"" ></script>"
Dim headscript As LiteralControl = New LiteralControl()
headscript.Text = String.Format(scriptFormat, ResolveUrl(scriptRoot + scriptName))
Page.Header.Controls.Add(headscript)
End Sub
It is now simple to add my scripts like so:
Private Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
RegisterJQueryScript("jquery.min.js", True)
RegisterJQueryScript("/js/jquery.dnntabs.js")
End Sub
Our tabs are now complete. Feel free to modify the JavaScript and html to suit your particular use case. Tabs are a great example of a feature that is easy to add to your web application and very rarely require the use of a third party component or control and are a great addition to any web developer’s toolkit.