Last active
March 14, 2022 09:44
-
-
Save predragnikolic/f50a7c31822fcb8bf64744316563adf9 to your computer and use it in GitHub Desktop.
Trap focus. Useful for trapping focus within a modal, or an open dropdown...
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// inspired by | |
// https://hiddedevries.nl/en/blog/2017-01-29-using-javascript-to-trap-focus-in-an-element | |
import { ReactNode, useEffect, useRef } from "react" | |
type FocusableHTMLElement = HTMLButtonElement | |
type Props = { | |
children: ReactNode | |
} | |
export function FocusTrap({ children }: Props) { | |
const focusTrapElRef = useRef<HTMLDivElement>(null) | |
useEffect(() => { | |
// currentlyFocusedEl is used to restore focus when the focus trap element is removed | |
const currentlyFocusedEl = document.activeElement as FocusableHTMLElement | null | |
const element = focusTrapElRef.current | |
if (!element) return | |
const 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])") | |
const firstFocusableEl = focusableEls[0] as FocusableHTMLElement | null | |
firstFocusableEl?.focus() | |
const onKeyDown = (e: KeyboardEvent) => { | |
const 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])") | |
const firstFocusableEl = focusableEls[0] as FocusableHTMLElement | null | |
const lastFocusableEl = focusableEls[focusableEls.length - 1] as FocusableHTMLElement | null | |
const KEYCODE_TAB = 9 | |
const isTabPressed = (e.key === "Tab" || e.keyCode === KEYCODE_TAB) | |
if (!isTabPressed) return | |
if ( e.shiftKey ) /* shift + tab */ { | |
if (document.activeElement === firstFocusableEl) { | |
lastFocusableEl?.focus() | |
e.preventDefault() | |
} | |
} else /* tab */ { | |
if (document.activeElement === lastFocusableEl) { | |
firstFocusableEl?.focus() | |
e.preventDefault() | |
} | |
} | |
} | |
element.addEventListener("keydown", onKeyDown) | |
return () => { | |
element.removeEventListener("keydown", onKeyDown) | |
currentlyFocusedEl?.focus() | |
} | |
}, [children]) | |
return <div ref={focusTrapElRef}>{children}</div> | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment