Accessible tabs
ARIA accessible tabs
by Andrew Bone
HTML
<div id="tab-example" class="tab-container">
<ul aria-controls="tab-example" role="tablist">
<li role="tab" aria-controls="tab-example_1" tabindex="0" aria-selected="false">Tab 1</li>
<li role="tab" aria-controls="tab-example_2" tabindex="0" aria-selected="true">Tab 2</li>
<li role="tab" aria-controls="tab-example_3" tabindex="0" aria-selected="false">Tab 3</li>
</ul>
<main>
<div id="tab-example_1" role="tabpanel" aria-expanded="false">
<p>
Lorem Ipsum "Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit..." "There is no one who loves pain itself, who seeks after it and wants to have it, simply because it is pain..." Lorem ipsum dolor sit amet, consectetur
adipiscing elit. Aenean ac pulvinar risus. Mauris finibus magna non diam laoreet, eget tempor ligula placerat. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec interdum, lacus id ultrices vehicula,
libero enim viverra urna, a pellentesque libero enim a sem. Cras sodales congue luctus. Nam quis porttitor diam, quis pretium odio. Curabitur tristique leo ac nibh vulputate mattis. Donec bibendum risus molestie, posuere dui at, feugiat dui. Sed
vitae efficitur neque.
</p>
</div>
<div id="tab-example_2" role="tabpanel" aria-expanded="true">
<p>
Aenean a augue sem. Interdum et malesuada fames ac ante ipsum primis in faucibus. Integer sollicitudin lacus dolor, sit amet auctor sem congue id. Praesent pharetra euismod facilisis. Maecenas aliquet, lorem sit amet molestie convallis, mi augue faucibus
nulla, non rutrum felis nisi sed quam. Proin luctus, enim eget congue venenatis, quam ex gravida quam, sit amet congue erat ipsum ut libero. Nam maximus elit quis nisl imperdiet, ornare faucibus nulla suscipit. Integer iaculis sagittis nulla vel
commodo. Donec laoreet commodo elementum. Integer ex eros, varius nec blandit eleifend, fermentum...
CSS
body {
background: #CCC;
}
.tab-container {
overflow: hidden;
background: #fff;
}
.tab-container [role=tablist] {
display: flex;
margin: 0;
padding: 0;
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23);
}
.tab-container [role=tab] {
position: relative;
list-style: none;
text-align: center;
cursor: pointer;
padding: 14px;
flex-grow: 1;
color: #444;
}
.tab-container [role=tab]:hover {
background: #eee;
}
.tab-container [role=tab][aria-selected=true] {
color: #000;
}
.tab-container [role=tab][aria-selected=true]::after {
content: "";
position: absolute;
width: 100%;
height: 4px;
background: #f44336;
left: 0;
bottom: 0;
}
.tab-container main {
padding: 0 1em;
position: relative;
}
.tab-container main [role=tabpanel] {
display: none;
}
.tab-container main [role=tabpanel][aria-expanded=true] {
display: block;
}
JavaScript
class TabController {
constructor(container) {
this.container = document.querySelector(container);
this.tablist = this.container.querySelector('[role=tablist]');
this.tabs = this.container.querySelectorAll('[role=tab]');
this.tabpanels = this.container.querySelectorAll('[role=tabpanel]');
this.activeTab = this.container.querySelector('[role=tab][aria-selected=true]');
this._addEventListeners();
}
// Private function to set event listeners
_addEventListeners() {
for (let tab of this.tabs) {
tab.addEventListener('click', e => {
e.preventDefault();
this.setActiveTab(tab.getAttribute('aria-controls'));
});
tab.addEventListener('keyup', e => {
if (e.keyCode == 13 || e.keyCode == 32) { // return or space
e.preventDefault();
this.setActiveTab(tab.getAttribute('aria-controls'));
}
})
}
this.tablist.addEventListener('keyup', e => {
switch (e.keyCode) {
case 35: // end key
e.preventDefault();
this.setActiveTab(this.tabs[this.tabs.length - 1].getAttribute('aria-controls'));
break;
case 36: // home key
e.preventDefault();
this.setActiveTab(this.tabs[0].getAttribute('aria-controls'));
break;
case 37: // left arrow
e.preventDefault();
let previous = [...this.tabs].indexOf(this.activeTab) - 1;
previous = previous >= 0 ? previous : this.tabs.length - 1;
this.setActiveTab(this.tabs[previous].getAttribute('aria-controls'));
break;
case 39: // right arrow
e.preventDefault();
let next = [...this.tabs].indexOf(this.activeTab) + 1;
next = next < this.tabs.length ? next : 0
this.setActiveTab(this.tabs[next].getAttribute('aria-controls'));
break;
}
})
}
// Public function to set the tab by id
// This can be called by the developer too.
setActiveTab(id)...