@mdbootstrap/bootstrap-dark-mode
Version:
Responsive Dark Mode theme built with Bootstrap 5 with Dark Mode toggle button that switches between dark and light themes.
98 lines (79 loc) • 2.64 kB
JavaScript
import SelectorEngine from '../dom/selector-engine';
import { isVisible } from './index';
class FocusTrap {
constructor(element, options = {}, toggler) {
this._element = element;
this._toggler = toggler;
this._event = options.event || 'blur';
this._condition = options.condition || (() => true);
this._selector =
options.selector || 'button, a, input, select, textarea, [tabindex]:not([tabindex="-1"])';
this._onlyVisible = options.onlyVisible || false;
this._focusableElements = [];
this._firstElement = null;
this._lastElement = null;
this.handler = (e) => {
if (this._condition(e) && e.target === this._lastElement) {
e.preventDefault();
this._firstElement.focus();
}
};
}
trap() {
this._setElements();
this._init();
this._setFocusTrap();
}
disable() {
this._focusableElements.forEach((element) => {
element.removeEventListener(this._event, this.handler);
});
if (this._toggler) {
this._toggler.focus();
}
}
update() {
this._setElements();
this._setFocusTrap();
}
_init() {
const handler = (e) => {
if (!this._firstElement || e.key !== 'Tab' || this._focusableElements.includes(e.target)) {
return;
}
e.preventDefault();
this._firstElement.focus();
window.removeEventListener('keydown', handler);
};
window.addEventListener('keydown', handler);
}
_filterVisible(elements) {
return elements.filter((el) => {
if (!isVisible(el)) return false;
const ancestors = SelectorEngine.parents(el, '*');
for (let i = 0; i < ancestors.length; i++) {
const style = window.getComputedStyle(ancestors[i]);
if (style && (style.display === 'none' || style.visibility === 'hidden')) return false;
}
return true;
});
}
_setElements() {
this._focusableElements = SelectorEngine.find(this._selector, this._element);
if (this._onlyVisible) {
this._focusableElements = this._filterVisible(this._focusableElements);
}
this._firstElement = this._focusableElements[0];
this._lastElement = this._focusableElements[this._focusableElements.length - 1];
}
_setFocusTrap() {
this._focusableElements.forEach((element, i) => {
if (i === this._focusableElements.length - 1) {
element.addEventListener(this._event, this.handler);
} else {
element.removeEventListener(this._event, this.handler);
}
});
}
}
export default FocusTrap;