Skip to content

Instantly share code, notes, and snippets.

@esthezia
Last active March 7, 2016 10:33
Show Gist options
  • Save esthezia/5804731 to your computer and use it in GitHub Desktop.
Save esthezia/5804731 to your computer and use it in GitHub Desktop.
Accessible Tabs (jQuery) - Minimal setup
<!DOCTYPE html>
<html lang="en" class="nojs">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<title>jQuery - Accessible Tabs</title>
<meta name="description" content="" />
<meta name="viewport" content="width=device-width, minimum-scale=1" />
<meta name="format-detection" content="telephone=no" />
<meta name="format-detection" content="address=no" />
<meta http-equiv="cleartype" content="on" />
<script>document.documentElement.className = "";</script>
<style>
a:focus { outline: thin dotted; }
.js-tabpanel { display: none; }
.is-tabpanel-selected { display: block; }
</style>
</head>
<body>
<!--[if lt IE 7]>
<p class="chromeframe">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> or <a href="http://www.google.com/chromeframe/?redirect=true">activate Google Chrome Frame</a> to improve your experience.</p>
<![endif]-->
<div class="js-tabs" id="tabs">
<div class="js-tablist" role="tablist">
<a href="?tab=1#tabs" class="js-tab is-tab-selected" id="tab-1" role="tab" aria-selected="true" aria-controls="tabpanel-1">Tab 1</a>
<a href="?tab=2#tabs" class="js-tab" id="tab-2" role="tab" aria-selected="false" aria-controls="tabpanel-2">Tab 2</a>
<a href="?tab=3#tabs" class="js-tab" id="tab-3" role="tab" aria-selected="false" aria-controls="tabpanel-3">Tab 3</a>
</div>
<div class="js-tabpanel is-tabpanel-selected" id="tabpanel-1" role="tabpanel" aria-labelledby="tab-1" aria-hidden="false">
Tabpanel 1
</div>
<div class="js-tabpanel" id="tabpanel-2" role="tabpanel" aria-labelledby="tab-2" aria-hidden="true">
Tabpanel 2
</div>
<div class="js-tabpanel" id="tabpanel-3" role="tabpanel" aria-labelledby="tab-3" aria-hidden="true">
Tabpanel 3
</div>
</div>
<script src="http://code.jquery.com/jquery-latest.min.js"></script>
<script>
var a = a || {};
a.jQuery = jQuery.noConflict(true);
a.jQuery(function ($) {
a.keys = {
"up": 38,
"down": 40,
"left": 37,
"right": 39,
"tab": 9,
"pageup": 33,
"pagedown": 34,
"end": 35,
"home": 36,
"enter": 13,
"backspace": 8,
"delete": 46,
"f5": 116
};
a.aKeys = [];
for (var k in a.keys) {
a.aKeys.push(a.keys[k]);
}
a.tabs = function (i, el) {
$(".js-tabs").each(function () {
var t = $(this),
nav = t.find(".js-tablist:first"),
panels = t.find(".js-tabpanel"),
navOnClass = "is-tab-selected",
panelOnClass = "is-tabpanel-selected",
navOn = nav.children("." + navOnClass),
panelOn;
// support tabs within tabs.
// remove tabpanels that don't belong to the current tabs container's functionality.
panels.each(function () {
if (!$(this).closest(".js-tabs").is(t)) {
panels = panels.not($(this));
}
});
panelOn = panels.filter("." + panelOnClass);
function selectTab () {
var tab = $(this),
i;
if (tab.hasClass(navOnClass)) {
return false;
}
navOn.removeClass(navOnClass);
navOn = tab;
i = navOn.index();
navOn.addClass(navOnClass);
panelOn.removeClass(panelOnClass).attr("aria-hidden", "true");
panelOn = panels.filter(":eq(" + i + ")");
panelOn.addClass(panelOnClass).attr("aria-hidden", "false");
return false;
}
// not using "focus" because it will also get triggered when the browser window gets focused,
// and this could negatively impact the behaviour.
// e.g. when tracking clicks on tabs (event tracking with Google Analytics)
nav.children().click(selectTab).keydown(function (e) {
var tab = $(this),
key = e.keyCode || e.which,
keys = a.keys,
nextA = tab.next(),
prevA = tab.prev(),
firstA = nav.children(":eq(0)"),
lastA = nav.children(":last"),
// ctrl = e.ctrlKey && !e.shiftKey && !e.altKey,
shift = e.shiftKey;
nextA = nextA.length === 1 ? nextA : nav.children(":first");
prevA = prevA.length === 1 ? prevA : nav.children(":last");
if (key === keys.home) {
firstA.triggerHandler("click");
firstA.get(0).focus();
return false;
}
if (key === keys.end) {
lastA.triggerHandler("click");
lastA.get(0).focus();
return false;
}
if ((key === keys.right) || (key === keys.down)) {
if (!nextA) { return true; }
nextA.triggerHandler("click");
nextA.get(0).focus();
return false;
}
if ((key === keys.left) || (key === keys.up)) {
if (!prevA) { return true; }
prevA.triggerHandler("click");
prevA.get(0).focus();
return false;
}
if ((key === keys.tab) && !shift) {
if (panelOn.find("input, a, select").first().length) {
panelOn.find("input, a, select").first().get(0).focus();
}
// return true because we don't want to block navigating by pressing the "tab" key
return true;
}
});
});
};
a.tabs();
});
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment