I use this plugin
export function trapFocus(element, cb) {
var focusableEls = element.querySelectorAll('a[href]:not([disabled]), button:not([disabled]), textarea:not([disabled]), input[type="text"]:not([disabled]), input[type="radio"]:not([disabled]), input[type="checkbox"]:not([disabled]), select:not([disabled])'),
firstFocusableEl = focusableEls[0];
lastFocusableEl = focusableEls[focusableEls.length - 1],
KEYCODE_TAB = 9,
ESCAPE_TAB = 27;
element.addEventListener('keydown', function(e) {
var isTabPressed = (e.key === 'Tab' || e.keyCode === KEYCODE_TAB);
var isEscPressed = (e.key === 'Escape' || e.keyCode === ESCAPE_TAB);
if(!isTabPressed && !isEscPressed) {
return;
}
if(isEscPressed) {
this.callback = cb;
this.callback();
}
if(e.shiftKey) /* shift + tab */ {
if(document.activeElement === firstFocusableEl) {
lastFocusableEl.focus();
e.preventDefault();
}
} else /* tab */ {
if(document.activeElement === lastFocusableEl) {
firstFocusableEl.focus();
e.preventDefault();
}
}
});
};
This basically is used as following
- Click a button and we manually shift focus to the first focusable element in a “container”.
- We trap the focus in this container, ultimately you loop in the container until you click a “close button”, and it takes you back to the open button. This is part of our accessibility.
The issue arises if the last "focusable element is actually hidden.
E.g. the + sign on the last link (which is a button) indicates that there’s a hidden link. That being hidden breaks the plugin. I’m not sure how to even approach this in Javascript. It’s a dynamic situation and sometimes the last focusable element actually is visible . There might be multiple links in that last dropdown.