|
/** |
|
* Figure out if the current element has a next sibling. |
|
* If so, moving focus to it. |
|
* @param {HTMLElement} parent |
|
* @param {string} target |
|
*/ |
|
const focusNextItem = (parent, target) => { |
|
const item = document.activeElement |
|
const nextIsVisible = item.nextElementSibling.offsetWidth > 0 && item.nextElementSibling.offsetHeight > 0; |
|
|
|
if (item.nextElementSibling.hasAttribute('tabindex') && item.nextElementSibling && !item.nextElementSibling.hasAttribute('disabled') && nextIsVisible) { |
|
activate(parent, target, item.nextElementSibling) |
|
} |
|
} |
|
|
|
/** |
|
* Figure out if the current element has a previous sibling. |
|
* If so, moving focus to it. |
|
* @param {HTMLElement} parent |
|
* @param {string} target |
|
*/ |
|
const focusPreviousItem = (parent, target) => { |
|
const item = document.activeElement |
|
const prevIsVisible = item.previousElementSibling.offsetWidth > 0 && item.previousElementSibling.offsetHeight > 0; |
|
|
|
if (item.previousElementSibling.hasAttribute('tabindex') && item.previousElementSibling && !item.previousElementSibling.hasAttribute('disabled') && prevIsVisible) { |
|
activate(parent, target, item.previousElementSibling) |
|
} |
|
} |
|
|
|
/** |
|
* Here is where the roving tabindex magic happens! |
|
* @param {HTMLElement} parent |
|
* @param {string} target |
|
* @param {HTMLElement} item |
|
*/ |
|
const activate = (parent, target, elementSibling) => { |
|
// Set all the target elements to tabindex -1 |
|
parent.querySelectorAll(target).forEach(el => (el.tabIndex = -1)) |
|
|
|
// Make the current target element to "active" |
|
elementSibling.tabIndex = 0 |
|
elementSibling.focus() |
|
} |
|
|
|
/** |
|
* Enable the roving tabindex and set the control keys |
|
* @param {Node} parent The element node |
|
* @param {String} nextKey The event.key name to select next item |
|
* @param {String} prevKey The event.key name to select previous item |
|
* @param {String} element The type of child elements to be selectable |
|
*/ |
|
|
|
const rovingTabindex = (parent, nextKey, prevKey, element) => { |
|
/** |
|
* Auto set the tabindex order to prepare |
|
* the roving tabindex |
|
*/ |
|
|
|
const iterables = parent.querySelectorAll(element); |
|
iterables.forEach((item, index) => { |
|
item.setAttribute('tabindex', index === 0 ? '0' : '-1') |
|
}) |
|
|
|
/** |
|
* Activate the roving tabindex navigation |
|
*/ |
|
parent.addEventListener('keydown', (event) => { |
|
switch (event.key) { |
|
case nextKey: |
|
event.preventDefault() |
|
focusNextItem(parent, element) |
|
break |
|
case prevKey: |
|
event.preventDefault() |
|
focusPreviousItem(parent, element) |
|
break |
|
default: |
|
break |
|
} |
|
}, true) |
|
} |
|
|
|
export default rovingTabindex; |