Subscribe to my Feed, follow me on Twitter, recommend me on Working With Rails or see my code on GitHub
Unobtrusive Ajax patterns: tabs
(Ok, there’s not one bit of Ajax in this, but I’m just trying to keep it consistent :)
I guess you know what tabs are: There are two main components, the tabs themselves and the panes they control. To “tabify” an HTML element, we need to know which element is the container (like a form) and which elements are the tab panes and the titles to be used in the tabs. Let’s use a form as an example container:
<form class="tabs">
<fieldset class="tab">
<legend class="title">Tab 1</legend>
<p><label>Koda</label><input type="text" /></p>
<p><label>Basanda</label><input type="text" /></p>
</fieldset>
<fieldset class="tab">
<legend class="title">Tab 2</legend>
<p><label>Bosoya</label><input type="text" /></p>
<p><label>Tikki</label><input type="text" /></p>
</fieldset>
<p class="button">
<input type="submit" value="Ok" />
</p>
</form>
Here you can see that the fieldsets are marked as tabs. Actually they’re tab panes, we’ll generate the tabs from the title elements (legend).
document.observe('dom:loaded', function(){
//Find all elements with the "tabs" class (the containers)
$$('.tabs').each(function(container){
var tabs = container.select('.tab');//Panes
//Create a list to contain the tab elements
var tabList = new Element('ul', {'class':'tablist'});
//Go through each tab pane
tabs.each(function(tab){
var li = new Element('li');//A tab
li.update(tab.down('.title').innerHTML);
//Observe the click event, this changes the active tab
li.observe('click', function(){
//First, inactivate all tabs
tabList.select('li').invoke('removeClassName', 'active');
//Then, activate this tab
li.addClassName('active');
//Inactivate all tab panes
tabs.invoke('removeClassName', 'active');
//Activate the associated tab pane
tab.addClassName('active');
});
tabList.insert({bottom:li});//Add the tab to the list
});
container.addClassName('enhanced');//Make it targetable with CSS
container.insert({top:tabList});//Add the tablist to the top of the container
//Activate first tab
tabs.first().addClassName('active');
tabList.down('li').addClassName('active');
});
});
Now all you need to do is add some CSS:
.tabs .tablist {
list-style-type: none;
margin: 0 !important;
overflow: hidden;
}
.tabs .tablist li {
float: left;
background: #ccc;
border: 1px outset #ccc;
border-bottom: 1px solid #bbb;
color: #333;
padding: 0.25em;
margin-right: 2px;
}
.tabs .tablist li:hover {
cursor: pointer;
text-decoration: underline;
}
.tabs .tablist li.active {
background-color: #ddd;
border-color: #ddd;
}
.tabs.enhanced .tab {
display: none;
background: #ddd;
border: 1px outset #ddd;
border-top: none;
padding: 1em;
}
.tabs.enhanced .title {
display: none;
}
.tabs.enhanced .tab.active {
display: block;
}
And then you have tabs:
Of course this can be organised and abstracted more to make it seem a bit more object-oriented, but that’s the basics of it.