switchable

GPL3

This is a generic script for tabbed content. Here's a live example. You can find the script with full instructions here, or everything you'll need below (version 1.0.6).

Installation

This acts on any element with the 'switch' class inside any element with the 'switchable' class. The initial visible section ('switch' element) is the first one; if you want, you can specify a different one in the 'visiblesection' attribute of the 'switchable' element, using a number, starting from 0.

The 'switch' elements can be given names through the 'title' attribute. Example:

<div class="switchable" visiblesection="1">
    <div class="section" sectionname="first section">
        This is the first section, called 'first section'.
    </div>
    <div class="section" sectionname="second">
        This is the second section, called 'second'.  It is the initial visible
        section.
    </div>
    <div class="section">
        This is the third section, called '2' because it has not been given a
        name.
    </div>
</div>

Alter some variables as detailed at the top of the code below if you want, then place this script anywhere on the page, like this:

<script type="text/javascript"> [script here] </script>

or place this in a file (conventionally with the '.js' extension) and use:

<script type="text/javascript" src="[file path]"></script>

The script tries to use Tanny O'Haley's domReady (download from here and include to make stuff load earlier). If it fails, it tries to use the browser's addEventListener or attachEvent, or MediaWiki's addOnloadHook, before defaulting to onload, so look out for any other scripts that use this as it'll cause conflicts.

The resulting 'tabs' are just an unordered (bulleted) list; you can style these with CSS. Here's something grey and generic:

.switchabletabs li {
    list-style: none;
    display: inline;
    margin-right: .5em;
    padding: .1em .5em;
    border: 1px solid #aaa;
    background: #f2f2f2;
}

Code:

// change this to alter the text of links that show sections ('show ', for
// example)
var switchableAnchorPrefix = '';
// change the classes we look for if you want
var switchableClass = 'switchable';
var switchableSectionClass = 'switch';
var switchableTabsClass = 'switchabletabs';
// if you're okay with using non-standard HTML attributes, set this to
// something like 'sectionname' to avoid the tooltip thing you get with 'title'
var switchableSectionTitleAttr = 'title';


// I'd extend Node.prototype, but apparently IE fails...
function myGetElementsByClassName (node, cls) {
    var result = [];
    var pool = node.getElementsByTagName("*");
    var re = new RegExp('\\b' + cls + '\\b');
    for (var i = 0; i < pool.length; i++)
        if (re.test(pool[i].className)) result.push(pool[i]);
    return result;
}

var switchable = {};

switchable.createTab = function (label, isAnchor, i, j) {
    var tab = document.createElement('li');
    var child;
    if (isAnchor) {
        child = document.createElement('a');
        child.href =
            'javascript:switchable.setVisible(' + i + ', ' + j + ');';
    } else child = document.createElement('strong');
    child.appendChild(document.createTextNode(label));
    tab.appendChild(child);
    return tab;
};

switchable.getVisible = function (i) {
    var visible = this.switchers[i].getAttribute('visiblesection');
    if (visible) visible = parseInt(visible);
    if (visible === null || isNaN(visible)) {
        visible = 0;
        this.switchers[i].setAttribute('visiblesection', visible.toString());
    }
    return Math.max(0, Math.min(visible,
                                this.switchers[i].sections.length - 1));
};

switchable.updateVisible = function (i) {
    if (isNaN(parseInt(i))) {
        // update all switchables if no valid number given
        for (var i = 0; i < this.switchers.length; i++)
            this.updateVisible(i);
    } else {
        var visible = this.getVisible(i);
        var sections = this.switchers[i].sections;
        var tc = this.switchers[i].tabContainer;
        var currentTab;
        for (var j = 0; j < sections.length; j++) {
            if (j == visible) {
                // change 'show' link
                currentTab = this.createTab(sections[j].sectionName,
                                                false);
                currentTab.j = j;
                tc.replaceChild(currentTab, tc.tabs[j]);
                // show section
                sections[j].style.display = '';
            } else {
                // change 'show' link
                if (tc.currentTab !== undefined && tc.currentTab.j == j)
                    tc.replaceChild(tc.tabs[j], tc.currentTab);
                // hide section
                sections[j].style.display = 'none';
            }
        }
        if (currentTab !== undefined) tc.currentTab = currentTab;
    }
};

switchable.setVisible = function (i, j) {
    this.switchers[i].setAttribute('visiblesection', j);
    this.updateVisible(i);
};

switchable.init = function () {
    // get the elements we care about
    var switchers = myGetElementsByClassName(document, switchableClass);
    switchable.switchers = switchers;
    for (var i = 0; i < switchers.length; i++) {
        var switcher = switchers[i];
        var sections = myGetElementsByClassName(switcher,
                                                switchableSectionClass);
        switcher.sections = sections;
        // create show/hide anchors
        var tabContainer = document.createElement('ul');
        tabContainer.className = switchableTabsClass;
        switcher.appendChild(tabContainer);
        switcher.tabContainer = tabContainer;
        tabContainer.tabs = [];
        for (var j = 0; j < sections.length; j++) {
            // re-append section to place it after links
            switcher.appendChild(sections[j]);
            // use section's name if it has one
            var sectionName = sections[j].getAttribute(
                                                switchableSectionTitleAttr);
            if (!sectionName) sectionName = j.toString();
            sections[j].sectionName = sectionName;
            // create anchor
            var tab = switchable.createTab(
                            switchableAnchorPrefix + sectionName, true, i, j);
            tabContainer.appendChild(tab);
            tabContainer.tabs.push(tab);
        }
    }
    // initial show/hide
    switchable.updateVisible();
};

// initialise on page load
if (window.domReady !== undefined) domReady(switchable.init);
else if (window.addEventListener !== undefined)
    addEventListener('load', switchable.init, false);
else if (window.attachEvent !== undefined)
    attachEvent('onload', switchable.init);
else if (window.addOnloadHook !== undefined) addOnloadHook(switchable.init);
else onload = switchable.init;