Skip to content

Instantly share code, notes, and snippets.

@deebloo
Created August 12, 2021 13:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save deebloo/b57ae126a7b4467bee175c5b6b112f49 to your computer and use it in GitHub Desktop.
Save deebloo/b57ae126a7b4467bee175c5b6b112f49 to your computer and use it in GitHub Desktop.
A utility class for creating and interacting with a focus trap. (ex. keep focus in a modal)
export function getFocusableEls(element: HTMLElement) {
return element.querySelectorAll<HTMLElement>(
'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])'
);
}
export class FocusTrap {
private focusableEls!: NodeListOf<HTMLElement>;
private firstFocusableEl?: HTMLElement;
private lastFocusableEl?: HTMLElement;
private listener = this.onKeyDown.bind(this);
constructor(private element: HTMLElement) {
this.update();
}
update() {
this.focusableEls = getFocusableEls(this.element);
this.firstFocusableEl = this.focusableEls[0];
this.lastFocusableEl = this.focusableEls[this.focusableEls.length - 1];
}
start() {
this.element.addEventListener('keydown', this.listener);
}
stop() {
this.element.removeEventListener('keydown', this.listener);
}
private onKeyDown(e: KeyboardEvent) {
if (e.key === 'Tab') {
if (e.shiftKey) {
// shift + tab
if (document.activeElement === this.firstFocusableEl) {
this.lastFocusableEl?.focus();
e.preventDefault();
}
} else {
// just tab
if (document.activeElement === this.lastFocusableEl) {
this.firstFocusableEl?.focus();
e.preventDefault();
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment