Tabbed Container

by Kyle Falconer

HTML

<script src="http://closure-library.googlecode.com/svn/trunk/closure/goog/base.js"></script>
<ul class="TabHeadings">
     <li><h2><span>Heading One</span></h2></li>
     <li><h2><span>Heading Two</span></h2></li>
     <li><h2><span>Heading 3</span></h2></li>
</ul>
<ul class="TabContents">
<li>
  <h3>Tab 1</h3>
  <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut ac mi nunc. Quisque imperdiet nibh sed neque bibendum aliquam. Ut tempor ligula in elit volutpat eu imperdiet enim faucibus. Maecenas et molestie sapien. Phasellus quis suscipit elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam vel nulla erat. Proin id tortor id augue imperdiet hendrerit non ut orci. Maecenas consectetur, massa eget consectetur pellentesque, tortor velit iaculis lectus, eu interdum metus felis ut leo.</p>
</li>
<li>
  <h3>Tab 2</h3>
<p>Nunc luctus commodo libero sed congue. Integer commodo convallis nibh eu placerat. Integer euismod, turpis et cursus semper, est lectus varius mi, ac iaculis tellus massa id massa. Aenean sed nibh velit, nec aliquam libero. Etiam mauris ligula, pharetra a eleifend eget, interdum a lectus. Sed ultrices, ipsum sed porta pharetra, ipsum velit cursus purus, vitae ultricies orci nisl quis mauris. Sed tincidunt tristique pulvinar. Fusce accumsan mollis metus id cursus. Nullam cursus pellentesque dui, vel feugiat metus tristique ut. Praesent et odio vitae lorem facilisis pulvinar sed eget erat. Nullam nulla mauris, aliquet sit amet dictum fermentum, eleifend ut justo. Sed faucibus dui ac lacus sollicitudin feugiat. Quisque nunc libero, rhoncus quis eleifend quis, consequat nec nisi. Aenean sodales commodo tincidunt. Nam sed velit eu velit pellentesque ultricies. Suspendisse pellentesque volutpat ultrices.</p>
</li>
 <li>
   <h3>Tab 3</h3>
<p>Nulla facilisi. Nam vel placerat augue. Morbi ullamcorper tempor nisl, et scelerisque lorem iaculis vitae. Vivamus nec luctus tortor. Vivamus mattis massa vitae enim vestibulum congue....

CSS

.TabHeadings { padding-left: 0; margin-bottom:-3px; border-bottom: 6px solid #5f6061; display: inline-block; width: 100%; }
.TabHeadings li { list-style: none outside none; float: left; border: 0px solid #e7e9e9; border-bottom: none; margin-top: 0; margin-right: 3px; margin-left: 3px; cursor: pointer; filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr='#eeefef',endColorstr='#c9cacc'); background-image: -moz-linear-gradient(top,#eeefef,#c9cacc); background-image: -ms-linear-gradient(top,#eeefef,#c9cacc); background-image: -webkit-linear-gradient(top,#eeefef,#c9cacc); background-image: linear-gradient(top,#eeefef,#c9cacc); background-color: #eeefef; -moz-border-radius-topleft: 5px; -webkit-border-top-left-radius: 5px; border-top-left-radius: 5px; -moz-border-radius-topright: 5px; -webkit-border-top-right-radius: 5px; border-top-right-radius: 5px; display: block; }
/*.TabHeadings:after{clear:both}*/
.TabHeadings li h2 { font-size: 12px; line-height: 12px; padding: 10px; margin: 0; color: #212122; }
.TabHeadings li h2:hover { text-decoration: underline; }

.TabHeadings .ActiveTab { background-color: #5f6061; background-image: none; filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr='#5f6061',endColorstr='#5f6061'); }
.TabHeadings .ActiveTab h2 { color: #ffffff; }

.TabContents { margin-top: -1px; padding: 9px; border:1px solid #D8D9DA; border-top:none; background-color: #F2F2F3; }
.TabContents li { margin: 0; }
.TabContents li *:first-child { margin-top: 0; }
.TabContents li *:last-child { margin-bottom: 0; }

JavaScript

goog.require('goog.debug.ErrorHandler');
goog.require('goog.dom');
goog.require('goog.dom.classes');
goog.require('goog.events');
goog.require('goog.events.EventHandler');
goog.require('goog.events.EventTarget');


/** 
*   @param {Element|null}    headings  The collection of nodes which have a 1:1 mapping with contents. These are the tabs.
*   @param {Element|null}   contents  The collection of nodes which have a 1:1 mapping with tabs. These are the contents.
*   @constructor
*/
tabbedContainer = function (headings, contents) {
    this.tHeadingItems = goog.dom.getChildren(headings);    // goog.dom.getChildren() requests an Element
    this.tContentItems = goog.dom.getChildren(contents);

    this.tabContentPair = new Array();

    for (var i = this.tHeadingItems.length - 1; i >= 0; i--) {
        var pairedTab = {
            tab: this.tHeadingItems[i],
          
            content: this.tContentItems[i]
        };
        this.tabContentPair.push(pairedTab);
    }
}


tabbedContainer.prototype.setupListeners = function () {

    //startup, displays default (first) tab
    this.showOnlyTab(this.tabContentPair);

    var tabPairs = goog.dom.getChildren(this.tabContentPair[0].tab.parentNode);
    // set up listeners, the final two arguments passed in to goog.events.listen allow "this" to be relative to the object rather than just the DOM node.
    for (var i = tabPairs.length - 1; i >= 0; i--) {
        goog.events.listen(this.tabContentPair[i].tab, goog.events.EventType.CLICK, this.showOnlyTab, false, this.tabContentPair[i]);
    }

}

/**
*   Event handler for tabbedContainer
*   @param {Object|Array} e The argument will be either an array of Objects containing  the two properites of the tab and content pair, or a single Object of the same kind.
*
*/
tabbedContainer.prototype.showOnlyTab = function (e) {
    var currentContentItemsArray, currentItem;
    if (e instanceof Array) {
        var contentsArray =...