baseframe-js
Version:
A suite of useful Javascript plugins and functions to help with Front-end Development on websites
71 lines (70 loc) • 2.6 kB
JavaScript
import $ from 'cash-dom';
import { isVisible } from "../util/helpers";
;
const defaultProps = {
focusFirst: true,
nameSpace: 'trapFocus',
focusable: ['button', 'a', 'input', 'select', 'textarea', '[tabindex]']
};
;
const canFocusEls = (i, el) => {
const baseFocusableRules = isVisible(el, true) && el.tabIndex !== -1;
const nodeName = el.nodeName.toUpperCase();
if ((nodeName === 'BUTTON' || nodeName === 'INPUT')) {
return baseFocusableRules && !el.disabled;
}
return baseFocusableRules && el.style.pointerEvents !== 'none';
};
const trapFocus = (modalEl, props) => {
const { focusFirst, focusable, nameSpace } = $.extend({}, defaultProps, props);
const $trapElem = $(modalEl);
const focusableJoined = typeof focusable === 'string' ? focusable : focusable.join(',');
const $firstFocusable = $trapElem.find(focusableJoined).filter(canFocusEls);
let firstFocusable = $firstFocusable.length ? $firstFocusable[0] : null;
if (focusFirst && firstFocusable) {
firstFocusable.focus();
}
$(document.body).on(`keydown.${nameSpace}`, function (e) {
const $focusable = $trapElem.find(focusableJoined).filter(canFocusEls);
if (!$focusable.length)
return;
const activeEl = document.activeElement;
const lastFocusable = $focusable[$focusable.length - 1];
const isTabPressed = e.key === 'Tab';
firstFocusable = $focusable[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 &&
document.activeElement.isSameNode(firstFocusable)) {
lastFocusable && lastFocusable.focus();
e.preventDefault();
}
}
else {
// if tab key is pressed
if (activeEl &&
lastFocusable &&
activeEl.isSameNode(lastFocusable)) {
firstFocusable && firstFocusable.focus();
e.preventDefault();
}
}
});
return {
remove: () => {
$(document.body).off(`keydown.${nameSpace}`);
}
};
};
export default trapFocus;