baseframe-js
Version:
Baseframe JS is a comprehensive suite of modular plugins and utilities designed for front-end development
72 lines (71 loc) • 2.57 kB
JavaScript
import $be from 'base-elem-js';
;
const defaultProps = {
focusFirst: true,
nameSpace: 'focusTrap',
focusable: ['button', 'a', 'input', 'select', 'textarea', '[tabindex]']
};
;
const canFocusEls = (el) => {
const baseFocusableRules = $be.isVisible(el) && el.tabIndex !== -1;
const nodeName = el.nodeName.toUpperCase();
if ((nodeName === 'BUTTON' || nodeName === 'INPUT')) {
return baseFocusableRules && !el.disabled;
}
return baseFocusableRules && el.style.pointerEvents !== 'none';
};
const focusTrap = (elem, props) => {
const d = document;
const body = d.body;
const { focusFirst, focusable, nameSpace } = { ...defaultProps, ...props };
const $trapElem = $be(elem);
const focusableJoined = typeof focusable === 'string' ? focusable : focusable.join(',');
const $firstFocusable = $trapElem.find(focusableJoined).filter(canFocusEls);
let firstFocusable = $firstFocusable.hasEls ? $firstFocusable.elem[0] : null;
if (focusFirst && firstFocusable) {
firstFocusable.focus();
}
$be(body).on(`keydown.${nameSpace}`, function (e) {
const $focusable = $trapElem.find(focusableJoined).filter(canFocusEls);
if (!$focusable.hasEls)
return;
const activeEl = d.activeElement;
const lastFocusable = $focusable.elem[$focusable.size - 1];
const isTabPressed = e.key === 'Tab';
firstFocusable = $focusable.elem[0];
if (!isTabPressed) {
return;
}
if (activeEl.nodeName.toUpperCase() === 'BODY') {
// somehow we lost focus
// this can happen if the last element is disabled if it was focused
// so re-assign to the first element
firstFocusable && firstFocusable.focus();
e.preventDefault();
}
if (e.shiftKey) {
// if shift key pressed for shift + tab combination
if (activeEl &&
firstFocusable &&
d.activeElement === firstFocusable) {
lastFocusable && lastFocusable.focus();
e.preventDefault();
}
}
else {
// if tab key is pressed
if (activeEl &&
lastFocusable &&
activeEl === lastFocusable) {
firstFocusable && firstFocusable.focus();
e.preventDefault();
}
}
});
return {
remove: () => {
$be(body).off(`keydown.${nameSpace}`);
}
};
};
export default focusTrap;