Update: I have created a new page dedicated to xaseTabs: xaseTabs.noelboss.ch

Tabs (re)evented

Today I released my «Xtensible And Simple Evented Tabs» Plugin on jQuery.com. This plugin does one thing – and one thing very good: creating tab functionality. It does not provide any styling or has any superfluous effects but instead focuses on extensibility and flexibility.

By default, the xaseTabs plugin turns a list of elements and a bunch of div’s into a tab module:

<div>
	<ul class="tabs">
		<li>Tab 1</li>
		<li>Tab 2</li>
		<li>Tab 3</li>
	</ul>
	<div>
		Panel 1
	</div>
	<div>
		Panel 2
	</div>
	<div>
		Panel 3
	</div>
</div>     

All you need to do, to have this behave as tabs, are the following lines of code:

$(document).ready(function(){
	$('.tabs').xaseTabs();
});

The plugin is based on custom events which can be extended or overwritten without any changes of the plugin itself. The possibilities are endless; adding ajax to load the panel content, triggering a lightbox-resize after activating a tab, using the tabs as process-navigation and prevent clicks to the next steps… It’s all in your hands. Additionally you can provide some options as an object to customize the behavior of the plugin without writing you own extension.

Features

  • As simple as it gets.
  • Extremely extensible thanks to custom events.
  • Preload specific tabs via URL-Hashes. This even works with multiple Tab instances on a single page since you can define what instance should be selected.
  • Customizable trigger event: Define what event causes the trigger of activating a new tab. Changing tabs on hover? No problem.
  • Completely control how your tabs are activated: You can even use the xaseTabs plugin to create acordeon-style modules by overwriting the „activate“ event.
  • Completely control how your tabs look: xaseTabs comes with no CSS and HTML requirements whatsoever.
  • Nice Code. xaseTabs validate with jslint. and the development code is well commented.
  • xaseTabs is very Lightweight. It’s less than 4KB packed

Download and Installation

You can download xaseTabs from google code. Then go ahead and include your copy of jQuery and xaseTabs at the bottom of your page. Replace the path of the xaseTabs Plugin with whatever folder you have put the plugin in. Then, add you custom code to initialize the xaseTabs:

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js" type="text/javascript"> </script>
<script src="http://www.yourserver.com/js/jquery.xaseTabs.latest.js" type="text/javascript"> </script>
<script type="text/javascript">
   $('.tabs').xaseTabs({ 
	extend: xaseTabsExtension
   }); 
</script>

You can also checkout a working copy from the xaseTabs SVN Repository.

Options & Defaults

xaseTabs comes with a set of options to allow maximum flexibility – besides the ability to rewrite nearly the entire plugin via custom events. You can alter the defaults by providing your options as an object to xaseTabs:

$('.tabs').xaseTabs({
	tabSelector: 'li a',
	activeClass: 'act',
	autoInitialize: false
});
namespace {string}
Default: ‚xaseTabs‘ – Namespace, will be added to the all events and url-hashes…
tabSelector {string}
Default: ‚li‘ – The selector of tabs relative to the element the xaseTabs plugin is called on. Default is an „li“ inside the selected tab ul.
panelSelector {string}
Default: ‚~ div‘ – The Selector of panels relative to the element the xaseTabs plugin is called on. Default are siblings of the selected tab ul.
activeAttribute {string}
Default: ’selected‘ – Attribute to check if predefined tab should be selected via url-hash. Could also be „#“.
activeClass {string}
Default: ‚active‘ – Class name that’s assigned to active tabs and panels.
triggerEvent {string}
Default: ‚click‘ – Event that triggers the activate event. Namespace will be added.
activateBindFunction {function} (not implemented yet)
Default: null – If you want to bind the activate event on yourself you can use this function
hashAttribute {string}
Default: ‚.‘ – CSS-Selector Attribute that will be checked to match a URL hash (#xaseTabsHASHATTR-1) for activating a specific tab. Alternative: „#“.
autoInitialize {boolean}
Default: true – If you want to trigger the initialization phase manually, set this flag to false.
extend {function}
Default: null – Here, you can pass a function that extends the default customEvents object with your own functions. Please reade the next chapters for detailed instruction on how to create such a function.

How to Extend the Plugin

xaseTabs is based on custom events – that’s why it’s so extensible. The custom events life inside a private customEvents object:

var customEvents = {
    initialize: [function(options) {
        this.bind("initialize." + options.namespace,
        function(e) {
            // ...
        });
    }],

    setupPanels: [function(options) {
        this.bind("setupPanels." + options.namespace,
        function(e) {
        	// ...
        });
    }],

    setupTabs: [function(options) {
        this.bind("setupTabs." + options.namespace,
        function(e) {
			// ...
        });
    }],

    activate: [function(options) {
        this.bind("activate." + options.namespace,
        function(e, selected) {
        	// ...
        });
    }]
};  

For a detailed description of this functions, read the next chapter „Custom Events – here to serve you„. Those events will be bound onto the calling element upon initialisation. Before the binding happens, you can extend that array and add your own functions or disable the default functions altogether with e.preventDefault(); How would you do this? Simply create a function that gets the „customEvents“ object passed and add this to the configuration object inside the „extend“ key. Within the customEvents object you can push or unshift new functions into the function arrays. Lets say you have the Tabs-Plugin inside a lightbox – and that lightbox should adapt the size of the panel upon changing a tab. You would simply insert a function that calls the resize function after the default activate function has been triggered. So you push a function to the end of the customEvents.activate array that will be called after the tab has been activated:

// Create a functions that receives the customEvents
var xaseTabsExtension = function(customEvents){
	// Select the desired function and add functionality:
	customEvents.activate.push(function(options) {
    	// Bind your new extension to the Element that calls 
		// the tabs (not the individual tabs!)
	    this.bind("activate." + options.namespace,
	    function(e, selected) {
	        // Do whatever you like upon the "activate"
	        // event has been triggered
	        $.fancybox.resize();
	    });
	});                 
	// don't forget to return the modified event Object
	return customEvents;
}   

After you have done so, you can call the xaseTabs function and pass this function withhin your options-object inside the extend key:

				
$('.tabs').xaseTabs({ 
	extend: xaseTabsExtension
});   

If you’d like to add ajax functionality, you would unshift (instead of push) the customEvents.activate array, and add this ajax-function before the default function to loads the content dynamically…

For the concept behind evented progarming, read this article by Yehuda Katz.

Custom Events – here to serve you

The xaseTabs plugin has four events that you can extend. In every event, you have the following variables at hand:

this {object}
Contains the object on which the xaseTabs plugin was called. „this“ contains also all relations to the individual tabs and panels.
$(this).data(‚$tabs‘) {object}
Contains all tabs associated with this specific tabs instance. Each tab contains a reference to the according panel via $tab.data(‚$panel‘)
$(this).data(‚$panels‘) {object}
Contains all panels associated with this specific tabs instance.
$(this).data(‚$tabs‘).eq(0).data(‚$panel‘) {object}
Each tab contains a reference to the according panel via .data(‚$panel‘). With this reference, you always know what panel belongs to which tab.
  • initialize
  • setupPanels
  • setupTabs
  • activate

Handles the initialization of the plugin: finding the panes and tabs, and triggering the setup events and activate the initial tab:

options {object}
Options provided to the Plugin including Defaults
e {object}
Event Object, call e.isDefaultPrevented() to check if Default is prevented
initialize: [function(options) {
    this.bind("initialize." + options.namespace,
    function(e) {
        if (e.isDefaultPrevented()) {
            return;
        }

        // caching an get cached elements from tabs-element
        var $t = $(this);
        var $tabs = $(options.tabSelector, $t);
        var $panels = $(options.panelSelector, $t);
        var $selected = $tabs.filter('.' + options.activeClass).eq(0);

        // We cache the tabs and the panels directly onto the element so with
        // $(this).data('$tabs') you always know what your tabs and panels are...
        $t.data('$panels', $panels).data('$tabs', $tabs);

        // setting up individual tabs
        $t.trigger("setupPanels." + options.namespace);

        // setting up individual panels
        $t.trigger('setupTabs.' + options.namespace);

        /** 
         * check for a hash like this: #xaseTabsTABID-2 where TABID is the Class of this tab-module
         * and the number behind the - the Tab to be activated
         */
        var hash = window.location.hash.split(options.namespace);
        if ($.isArray(hash)) {
            for (var i = hash.length - 1; i > 0; i--) {
                // loop trough all modules
                var module = hash[i].split('-');
                // get Tab number and Module ID
                if ($t.is(options.hashAttribute + module[0])) {
                    // if we are the the current module
                    // update selected Element and add class active
                    $selected = $(this).data('$tabs').removeClass(options.activeClass).eq(module[1] - 1).addClass(options.activeClass);
                }
            }
        }
        // search for predefined activate Elements
        $selected = $selected.length < 1 ? $tabs.filter("[" + options.activeAttribute + "=" + options.activeAttribute + "]").eq(0) : $selected;
        if ($selected.length < 1) {
            // If no element selected, we activate Tab 1
            $selected = $tabs.eq(0);
        }
        // trigger activate Event
        if ($selected.length > 0) {
            $t.trigger("activate." + options.namespace, $selected);
        }
    });
}]             

Saves the panel to the according tab:

options {object}
Options provided to the Plugin including Defaults
e {object}
Event Object, call e.isDefaultPrevented() to check if Default is prevented
setupPanels: [function(options) {
    this.bind("setupPanels." + options.namespace,
    function(e) {
        if (e.isDefaultPrevented()) {
            return;
        }
        var $t = $(this);
        var $tabs = $t.data('$tabs');
        var $panels = $t.data('$panels');

        $tabs.each(function(i) {
            $(this).data('$panel', $panels.eq(i));
        });
    });
}]  

Binds the custom activate event to the tabs. You can configure, what event triggers the activate-event via the options.triggerEvent option:

options {object}
Options provided to the Plugin including Defaults
e {object}
Event Object, call e.isDefaultPrevented() to check if Default is prevented
setupTabs: [function(options) {
    this.bind("setupTabs." + options.namespace,
    function(e) {
        var $t = $(this);
        var $tabs = $t.data('$tabs');
        $tabs.bind(options.triggerEvent + "." + options.namespace,
        function() {
            $t.trigger("activate." + options.namespace, $(this));
            return false;
        });
    });
}]   

This event deactivates all tabs and activates the selected tab. You get a special variable selected that contains the selected tab:

options {object}
Options provided to the Plugin including Defaults
e {object}
Event Object, call e.isDefaultPrevented() to check if Default is prevented
selected {object}
DOM-Object; tab that was selected. You need to jQuerify it before using it: $(selected)
activate: [function(options) {
    this.bind("activate." + options.namespace,
    function(e, selected) {
        if (e.isDefaultPrevented()) {
            return;
        }
        var $t = $(this);
        var $selected = $(selected);
        $t.data('$panels').hide();
        $t.data('$tabs').removeClass(options.activeClass);

        $selected.data('$panel').show();
        $selected.addClass(options.activeClass);
    });
}] 

Feedback, Bug-Reports & Feature Requests

If you have any feedback, go ahead and leave a comment, or write me an e-mail to „jquery at noelboss . ch“. If you would like to see a feature added, or if you have found a bug, let me know…

Submit your cool Extensions!

If you have written a cool extension for the xaseTabs plugin – let’s say it now produces coffee on every tab-click –then, send me a copy in order that I can link it here! Some ideas for extension:

  • Adding keyboard support
  • Saving state of the tabs via cookie
  • Adding history support (with BBQ)

F.A.Q.

» How do I manually activate a Tab?

Just select the Tab and call the click event (or whatever event you configured with options.triggerEvent)

$(.tabs li:eq(3)).click();

» How is this accordion thing done on this page?

With the xaseTabs-plugin – of-course…

$(document).ready(function(){
	var xaseTabsExtension = function(customEvents) {
		customEvents.activate.unshift(function(options) {
			this.bind("activate."+options.namespace,
				function(e, selected) {
					e.preventDefault();
					var $t = $(this);
					var $selected = $(selected);
					var $panel = $selected.data('$panel');
					$t.data('$panels').not($panel).slideUp();
					$panel.slideDown();
					$t.data('$tabs').removeClass("active");
					$selected.addClass("active");    
			});
		});
		return customEvents;
	};        
	$('.tabs').xaseTabs({ 
		extend: xaseTabsExtension
	});  
});

» Why don’t you add (insert your feature here) feature to the plugin?

xaseTabs is so extensible that you would be better of implementing that specific feature by yourself – I want to keep xaseTabs lean and clean. If you have a cool extension to this plugin, go ahead and comment or send me an e-mail or leave a comment…

» What does «xase» mean?

eXtensible And Simple Evented. It’s based on „evented programing

» How can I contact you?

Write a comment or send me an e-mail to jquery at noelboss . ch

» xaseTabs is so cool, can I donate some money?

Course you can – go ahead!