vanilla-framework
Version:
A simple, extendable CSS framework.
112 lines (97 loc) • 3.07 kB
JavaScript
const keys = {
left: 'ArrowLeft',
right: 'ArrowRight',
};
const direction = {
ArrowLeft: -1,
ArrowRight: 1,
};
/**
Attaches a number of events that each trigger
the reveal of the chosen tab content
@param {Array} tabs an array of tabs within a container
*/
function attachEvents(tabs) {
tabs.forEach(function (tab, index) {
tab.addEventListener('keyup', function (e) {
if (e.code === keys.left || e.code === keys.right) {
switchTabOnArrowPress(e, tabs);
}
});
tab.addEventListener('click', function (e) {
e.preventDefault();
setActiveTab(tab, tabs);
});
tab.addEventListener('focus', function () {
setActiveTab(tab, tabs);
});
tab.index = index;
});
}
/**
Determine which tab to show when an arrow key is pressed
@param {KeyboardEvent} event
@param {Array} tabs an array of tabs within a container
*/
function switchTabOnArrowPress(event, tabs) {
var pressed = event.code;
if (direction[pressed]) {
var target = event.target;
if (target.index !== undefined) {
if (tabs[target.index + direction[pressed]]) {
tabs[target.index + direction[pressed]].focus();
} else if (pressed === keys.left) {
tabs[tabs.length - 1].focus();
} else if (pressed === keys.right) {
tabs[0].focus();
}
}
}
}
/**
Cycles through an array of tab elements and ensures
only the target tab and its content are selected
@param {HTMLElement} tab the tab whose content will be shown
@param {Array} tabs an array of tabs within a container
*/
function setActiveTab(tab, tabs) {
tabs.forEach(function (tabElement) {
var tabContent = document.getElementById(tabElement.getAttribute('aria-controls'));
if (tabElement === tab) {
tabElement.setAttribute('aria-selected', true);
tabContent.removeAttribute('hidden');
} else {
tabElement.setAttribute('aria-selected', false);
tabContent.setAttribute('hidden', true);
}
});
}
/**
Attaches events to tab links within a given parent element,
and sets the active tab if the current hash matches the id
of an element controlled by a tab link
@param {String} selector class name of the element
containing the tabs we want to attach events to
*/
export function initTabs(selector) {
const tabContainers = [].slice.call(document.querySelectorAll(selector));
tabContainers.forEach(function (tabContainer) {
const tabs = [].slice.call(tabContainer.querySelectorAll('[aria-controls]'));
attachEvents(tabs);
});
}
/**
* Auto-initialize tabs when DOM is loaded
* This maintains backward compatibility for existing implementations
*/
export function autoInit() {
document.addEventListener('DOMContentLoaded', function () {
initTabs('[role="tablist"]');
});
}
// Export individual functions for more granular usage
export {attachEvents, switchTabOnArrowPress, setActiveTab};
// Auto-initialize by default to maintain backward compatibility
if (typeof document !== 'undefined') {
autoInit();
}