Sneaky Abstractions

Subscribe to my Feed, follow me on , recommend me on Working With Rails or see my code on GitHub

Unobtrusive Ajax patterns: tabs

Posted on July 03, 2008 10:32 Tagged with tabs, javascript, ajax patterns.

(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:

Tab 1

Tab 2

Tab 3

Tab 4

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.

I’m Sheriff John Bunnell, most people don’t even think about law enforcement until they either do something wrong, or get wronged them self. But police know that fighting crime is a full time job. If something isn’t going down now, it will be soon and officers have to be ready. So get ready, what we’re going to show you tonight is the ruthlessness of criminals, the toughness of cops and the clash if both sides refuse to back down.