Tabpanel Widget

Use the tab key to arrive at the active tab, then use the arrow keys to select other tabs.

Making content perceivable means making the output available to the user's senses, namely sight, sound, and touch (in the case of people who use Braille output devices). We won't worry about tasting or smelling web pages!

HTML Source Code

<div class="tabWidgetContainer">
    <ul class="tablist" role="tablist">
        <li class="active" role="tab" aria-selected="true" 
            aria-controls="perceivable" tabindex="0">Perceivable</li>
        <li class="inactive" role="tab" aria-selected="false" 
            aria-controls="operable" tabindex="-1">Operable</li>
        <li class="inactive" role="tab" aria-selected="false" 
            aria-controls="understandable" tabindex="-1">Understandable</li>
        <li class="inactive" role="tab" aria-selected="false" 
            aria-controls="robust" tabindex="-1">Robust</li>
    </ul>
    <div class="tabgrouping">
        <div id="perceivable" class="tabpanel active" role="tabpanel" aria-hidden="false">
          <p>Making content <em>perceivable</em> means making the <strong>output</strong> 
          available to the user's senses, namely sight, sound, and touch 
          (in the case of people who use Braille output devices). We won't worry about 
          tasting or smelling web pages!</p>
        </div>
        <div id="operable" class="tabpanel inactive" role="tabpanel" aria-hidden="true">
           <p>Making content <em>operable</em> means making the <strong>input mechanisms</strong> 
           robust enough to accept a wide range of devices and methods, including keyboard, 
           mouse, touch, gestures, single-switch devices, and so on.</p> 
        </div>
        <div id="understandable" class="tabpanel inactive" role="tabpanel" aria-hidden="true">
          <p>Making content <em>understandable</em> means making the message and the interface 
          easy to use and comprehend.</p>
        </div>
        <div id="robust" class="tabpanel inactive" role="tabpanel" aria-hidden="true">
          <p>Making content <em>robust</em> means ensuring it works across a wide range of 
          devices, with both forward and backward compatibility.</p>
        </div>
    </div>
</div>

JavaScript Source Code

"use strict";

var initTabPanel = function() {
    $('.tablist > li')
        .keydown(tabListKeyPress)
        .click(tabListClick);
}

var tabListKeyPress = function(event) {
    if (event.which === 37 || event.which === 38 || 
        (event.which === 33 && event.ctrlKey)) { //left/up/ctrl+pageup
        var prevItem = $(event.currentTarget).prev("li");
        if (prevItem.length > 0) {
            focusTab(prevItem);
			event.preventDefault(); 
        } else {
            //go to the last one
            var lastItem = $(event.currentTarget).siblings("li").last();
            focusTab(lastItem);
			event.preventDefault(); 
        }
    } else if (event.which === 39 || event.which === 40 || 
               (event.which === 34 && event.ctrlKey)) { //right/down/ctrl+pagedown
        var nextItem = $(event.currentTarget).next("li");
        if (nextItem.length > 0) {
            focusTab(nextItem);
			event.preventDefault(); 
        } else {
            //go to the first one
            var firstItem = $(event.currentTarget).siblings("li").first();
            focusTab(firstItem);
			event.preventDefault(); 
        }

    }
}

var tabListClick = function(event) {
    focusTab($(event.currentTarget));
}

var focusTab = function(newTab) {
    // Identify existing focus tab and: 1) Unset aria-selected, 2) set tabindex=-1, 3) replace
    // active class with inactive on both the tab and the panel, 4) set aria-hidden on panel
    var activeTab = $('.tablist > li.active');
    
    activeTab
        .addClass("inactive")
        .removeClass("active")
        .attr("aria-selected", "false")
        .attr("tabindex", "-1");

    $("#" + activeTab.attr("aria-controls"))
        .addClass("inactive")
        .removeClass("active")
        .attr("aria-hidden", "true");

    // For newly focused tab: 1) Set aria-selected, 2) set tabindex=0, 3) replace inactive class
    // with active on both tab and panel, 4) unset aria-hidden on panel
    
    newTab
        .addClass("active")
        .removeClass("inactive")
        .attr("aria-selected", "true")
        .attr("tabindex", "0");

    $("#" + newTab.attr("aria-controls"))
        .addClass("active")
        .removeClass("inactive")
        .attr("aria-hidden", "false");

    newTab.focus();
}

$(document).ready(initTabPanel);

CSS Source Code

.tabWidgetContainer {
	padding: 20px;
	background-color: #eee;
	border-radius: 10px;
	border: 1px solid #ccc;
}
.tablist {
	list-type:none;
	margin: 0 0 0 20px;
	padding: 0;
}
.tablist li {
	float: left;
	position: relative;
	top: 1px;
	margin: 0 5px 0 0;
	padding: 6px 9px 3px 9px;
	list-style-type: none;
	background: #fff;
	border: 1px solid #d9d9d9;
	border-radius: 10px 10px 0 0;
	border-bottom: none;
}
.tablist li:hover {
	cursor:pointer;
}
.tablist li:focus {
	outline: 2px solid #8cc63f;
	outline-offset: 2px;
}
.tablist li.active {
	position: relative;
	top: 1px;
	padding: 7px 9px 4px 9px;
	font-weight: bold;
}
.tabgrouping {
	z-index: 99;
	clear: both;
	background: #fff;
	padding: 30px;
	border: 1px solid #CCC;
	border-radius: 10px;
	box-shadow: #e8e8e8 0 0px 3px;
}
.tabpanel.inactive {
	display: none;
}
.tabpanel label.text {
	clear: both;
	width: 160px;
	float: left;
	text-align: right;
	padding: 0 2px 0 0;
}
.tabpanel input[type='text'] {
	width: 200px;
}
.tabpanel input[type='text'].long {
	width: 300px;
}
.tabpanel input[type='text'].short {
	width: 100px;
}
.tabpanel fieldset {
	border-radius: 10px;
	border: 1px solid #ccc;
}
.save {
	text-align: center;
	margin: 30px auto 5px auto;
}
.updating {
	display: none;
	text-align: center;
}
.updated {
	display: none;
	text-align: center;
	margin: 0 auto;
	width: 50%;
	padding: 10px;
	background: #fef6d6;
	color: #903;
	font-weight: bold;
	border: 2px solid #903;
	border-radius: 10px;
}

Copy and Paste Full Page Example