1

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.

Eden Chan
  • 111
  • 1
  • 6

1 Answers1

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