The code examples I have seen so far use libraries like JQuery and react-focus-lock https://www.npmjs.com/package/react-focus-lock I would really appreciate a simple example of two buttons in a modal such that you can't focus elements outside the modal by tabbing out.
Asked
Active
Viewed 882 times
1 Answers
0
I'm using following component to wrap the content where focus has to be trapped:
import { useRef, useEffect } from "react";
type Props = {
children: Array<JSX.Element | null>;
};
export const FocusTrap = (props: Props) => {
const ref = useRef<HTMLSpanElement>(null);
useEffect(() => {
let firstElement: (HTMLElement | undefined);
let lastElement: (HTMLElement | undefined);
if (ref.current != null)
for (const element of ref.current.querySelectorAll<HTMLElement>("[tabindex]"))
considerElement(element);
firstElement?.addEventListener("keydown", handleKeyOnFirst);
lastElement?.addEventListener("keydown", handleKeyOnLast);
return () => {
firstElement?.removeEventListener("keydown", handleKeyOnFirst);
lastElement?.removeEventListener("keydown", handleKeyOnLast);
};
function considerElement(element: HTMLElement) {
// @ts-ignore
if (!element.checkVisibility()) return;
if (firstElement === undefined) firstElement = element;
else lastElement = element;
}
function handleKeyOnFirst(event: KeyboardEvent) {
if (event.key !== "Tab" || !event.shiftKey) return;
event.preventDefault();
lastElement?.focus();
}
function handleKeyOnLast(event: KeyboardEvent) {
if (event.key !== "Tab" || event.shiftKey) return;
event.preventDefault();
firstElement?.focus();
}
}, [props.children]);
return <span ref={ref}>{props.children}</span>;
};
Used like this:
<FocusTrap>
<ModalOrOtherContentToTrapFocus/>
</FocusTrap>
The trap component scans underlying content finding first and last visible elements with tabIndex
attribute and overrides Tab/Shift+Tab behavior for them.
Be aware, that as of writing element.checkVisibility()
function is only available in Chrome. Related thread on SO: Check if element is visible in DOM.

Elringus
- 539
- 6
- 12