1 window.addEventListener('load', function() {
2 // Get relevant elements and collections
3 const allTabbed = document.querySelectorAll('.tabbed');
5 allTabbed.forEach((tabbed) => {
7 const tablist = tabbed.querySelector('ul');
8 const tabs = tablist.querySelectorAll('a');
9 const panels = tabbed.querySelectorAll('[id^="section"]');
11 // The tab switching function
12 const switchTab = (oldTab, newTab) => {
14 // Make the active tab focusable by the user (Tab key)
15 newTab.removeAttribute('tabindex');
16 // Set the selected state
17 newTab.setAttribute('aria-selected', 'true');
18 newTab.classList.add('active');
19 oldTab.removeAttribute('aria-selected');
20 oldTab.setAttribute('tabindex', '-1');
21 oldTab.classList.remove('active');
22 // Get the indices of the new and old tabs to find the correct
23 // tab panels to show and hide
24 let index = Array.prototype.indexOf.call(tabs, newTab);
25 let oldIndex = Array.prototype.indexOf.call(tabs, oldTab);
26 panels[oldIndex].hidden = true;
27 panels[index].hidden = false;
30 // Add the tablist role to the first <ul> in the .tabbed container
31 tablist.setAttribute('role', 'tablist');
33 // Add semantics are remove user focusability for each tab
34 Array.prototype.forEach.call(tabs, (tab, i) => {
35 tab.setAttribute('role', 'tab');
36 tab.setAttribute('id', 'tab' + (i + 1));
37 tab.setAttribute('tabindex', '-1');
38 // tab.setAttribute('class', 'active');
39 tab.parentNode.setAttribute('role', 'presentation');
41 // Handle clicking of tabs for mouse users
42 tab.addEventListener('click', e => {
44 let currentTab = tablist.querySelector('[aria-selected]');
45 if (e.currentTarget !== currentTab) {
46 switchTab(currentTab, e.currentTarget);
50 // Handle keydown events for keyboard users
51 tab.addEventListener('keydown', e => {
52 // Get the index of the current tab in the tabs node list
53 let index = Array.prototype.indexOf.call(tabs, e.currentTarget);
54 // Work out which key the user is pressing and
55 // Calculate the new tab's index where appropriate
56 let dir = e.which === 37 ? index - 1 : e.which === 39 ? index + 1 : e.which === 40 ? 'down' : null;
59 // If the down key is pressed, move focus to the open panel,
60 // otherwise switch to the adjacent tab
61 dir === 'down' ? panels[i].focus() : tabs[dir] ? switchTab(e.currentTarget, tabs[dir]) : void 0;
66 // Add tab panel semantics and hide them all
67 Array.prototype.forEach.call(panels, (panel, i) => {
68 panel.setAttribute('role', 'tabpanel');
69 panel.setAttribute('tabindex', '-1');
70 let id = panel.getAttribute('id');
71 panel.setAttribute('aria-labelledby', tabs[i].id);
75 // Initially activate the first tab and reveal the first tab panel
76 tabs[0].removeAttribute('tabindex');
77 tabs[0].setAttribute('aria-selected', 'true');
78 tabs[0].setAttribute('class', 'active');
79 panels[0].hidden = false;