Writing a simple, self contained tab container Dijit in Dojo

Over the last few weeks, I’ve been working with a new team and have been putting together some versatile dijits that are to be used throughout the UI. Today I spent some time working on an idea for a tab container which requires little or no customisation / interaction to get set up and running.

Additional Comment: The reason why I have done this over using the built in Dojo TabContainer is that I find it to be a bit ‘over the top’ for simple use as a plain tab container. I get faced with steep design guidelines by clients and even STEEPER browser requirements (such asIE6) and as a result, I find that it’s important to keep the memory / processor useage to a minimum and ensure that the fewest number of dom nodes are created. Thus, my ‘lite’ tabContainer was born!

I’ve spent the last hour or two re-writing a clean room version which I’d like to show to you all to demonstrate how easy it is to style and manipulate dom elements once you have them on the page.

Click to skip to the Demo Page

The idea was simple…
Get a tab container on a page, with tab switching, tab change events and a way to set which tab is shown externally.
The main requirement I gave myself what that it MUST be simple and self contained. In the past I’ve added tabs and panes to the page separately and then connected their events, but this proves fiddly and repetitive, so I’ve worked all of the variables into the dijit attributes, settable via it’s declarative marking! So…

What’s in the template…
…Not much

<div class="tabContainer">
	<ul dojoAttachPoint="_listNode"></ul>
	<div dojoAttachPoint="containerNode" class="content"></div>
</div>

An outer div with a class to restrict my CSS calls easily, a list to hold the tabs and a ‘content’ div with the magic attach point ‘containerNode’. As mentioned before, this is used throughout the dijit framework, and is where the innerHTML of your dijit declaration is copied to when the dijit is parsed. So this is the div into which our tab pane content will be put.

Now for the declaration…

<div dojoType="tom.dijit.TabContainer">
	<div tab="tab1" label="Tab One" selected="true">Content One</div>
	<div tab="tab2" label="Tab Two">Content Two</div>
	<div tab="tab3" label="Tab Three">Content Three</div>
</div>

As you can see, I’m putting using the ‘tab’ and ‘label’ attrs of the inner divs to define my parameters. The ‘tab’ param is the name that the tabContainer dijit will refer to the pane by, these need to be unique within each tabContainer level. The ‘label’ param is simply picked up and placed as the link text inside the tab and is the text that the user will see. And that’s all there is to it from a html point of view. This simple model allows n layer nesting of tab containers and makes it all very simple.

So how does that work…
Well, during the post create of the tabContainer dijit, I run a dojo query for all immediate children on my containerNode with the attribute ‘tab’. For each of these, I get hold of it’s tab and label attributes and create a list item and link for each result in the list node. I then add this info along with attach points to both the pane node and the tab node to a dijit level object and finally, I check to see if the pane has ‘selected==true’ and apply the selected class to it’s tab accordingly. If a pane is not ‘selected’, I hide it using a simple ‘display:none’ class named hidden.

postCreate : function() {
		this.inherited(arguments);
		
		//look for divs with a tab attr inside my container node!
		dojo.query("> div[tab]", this.containerNode).forEach(dojo.hitch(this, function(thisNode){
			//start building up our tab object
			var tabObj = {	"paneNode" : thisNode,
							"label" : dojo.attr(thisNode, "label") ? dojo.attr(thisNode, "label") : "Undefined" };
			//get the tab name, we'll need this!
			var tabName = dojo.attr(thisNode, "tab");
			
			//add a list item and an anchor tag to the list
			var tabItem = dojo.create("li", {"class":"tab"}, this._listNode);
			var tabLink = dojo.create("a", {"innerHTML": tabObj.label, "href":"javascript:;", "tab":tabName}, tabItem);
			tabObj.tabNode = tabItem;
			
			//connect the onclick event of our new link
			dojo.connect(tabLink, "onclick", this, "_onTabLinkClick");
			
			//now add our tabObj to the dijit level tabList so we can 
			//get to it later
			this._tabList[tabName] = tabObj;
			
			//get hold of the selected attr, to see if it should
			//be initially visible - last one found will win this battle
			var selected = dojo.attr(thisNode, "selected");
			
			if (!selected) {
				//not selected, so adding hidden class
				//(hidden class defined in global css as display:none)
				dojo.addClass(thisNode, "hidden");
			}
			else {
				//this ones selected, so put the name into our selectedTab var
				this._selectedTab = tabName;
				//add a 'selected' class to our tab list item
				dojo.addClass(tabItem, "selected");
			}
		}));		
	}

The result of all of this, is a very plain dom tree with all the elements we need to flesh out via CSS a lightweight, customisable tabContainer.

For the purposes of this demo, I have added a bounding border and a wrapper div to show the tabContainer’s containment.

So with a little bit of CSS effort, you can now transform this shell to any number of tabContainer layouts. For now, here’s a screenshot and a snippet of a small bit of CSS to give you a feel (bare in mind I spent 2 min on this CSS).
I hope to add another post in a few days to go over how to style the dijit and use the tabContainer’s events and functions to interact with a page controller (page level javascript).

Once again, I’ve used dashed borders to show where boundaries lie. The basic CSS is shown below

div.tabContainer > ul {
	list-style: none;
	padding: 0;
	margin: 0;
	height: 25px;
}

div.tabContainer > ul > li {
	float: left;
	height: 15px;
	padding: 5px;
	margin: 0px 5px;
	background: black;
}

div.tabContainer > ul > li > a {
	text-decoration: none;
	color: #EBEBED;
}

div.tabContainer > ul > li:hover,
div.tabContainer > ul > li:hover a {
	background: #EBEBED;
	color: #black;
}

div.tabContainer > ul > li.selected a {
	color: #729fcf;
	font-weight: bold;
}

Click the link to go take a look at a working demo (which I’ll upload shortly after completing this post!)
TabContainer Demo Page

TabContainer Full JS Source Code

Enjoy!