React-modal: I can't navigate trough form elements using tab button

Created on 12 Aug 2016  路  16Comments  路  Source: reactjs/react-modal

Summary:

Steps to reproduce:

  1. add some form elements inside a popup
  2. try to navigate with tab button

    Expected behavior:

focus should move trought the form elements

Most helpful comment

271 has been rebased and updated to meet style standards.

All 16 comments

I'm having the same issue as well. Does anyone know of a workaround in the meantime?

I can't reproduce what you are describing. I used the example implementation at https://reactcommunity.org/react-modal/ and the following was my experience with no issues.

https://cl.ly/1P0f1I1Y2Y1t

Can you provide a sample implementation of this not working properly?

It's part of an unreleased feature for an app I'm working on so it might be tricky. We have a form that we show in the page and also in a modal. When it's in a modal for some reason the tabs stop working. I'll see if I can come up with a reproducible example...

I'm seeing the same issue as well. Tracing it through, it comes at https://github.com/reactjs/react-modal/blob/master/lib/components/ModalPortal.js#L128 and then into the scopeTab and tabbable helpers. I'm unclear on the need for this special tab handling -- can anyone elaborate as to what this is trying to solve?

I've identified the issue I'm having, it is not solved by https://github.com/reactjs/react-modal/pull/216

In short, there is a check to see if an element is "tabbable", which includes various other checks, including "visible". This visible check traverses up the DOM looking for elements with display: none OR -- crucially -- offsetHeight of 0 or offsetWidth of 0. These values both being zero does not necessarily mean the children aren't visible! My project has various wrapper nodes that seem to be <span> with offset width and height of zero. This check should instead use el.getBoundingClientRect() which reports correct height and width.

The intention of the tab-handling code is to ensure you don't tab out of the modal. It loops you around back to the start when you leave the last "tabbable" item in the modal.

fix is here in latest: https://github.com/classdojo/react-modal

don't have time for a PR at this very moment, but if anyone else wants to use this version directly in package.json "react-modal": "classdojo/react-modal#fa2208611323bfeddea4011dac8c268cb9f3a56f" tell me if it fixes your problem!

Opened a separate PR for this, as the getBoundingClientRect method was still failing for us. There is a potential workaround in the PR #271

I am also having the same issue with this.

@mandazi which version are you using? I've updated the tabbable function recently.

See 0f2bf9e9.

@diasbruno 2.4.1

@diasbruno it looks like that update is for scopeTab, not tabbable. It's been a minute since I've looked at this, but if I recall correctly we're incorrectly identifying which elements are hidden with this check here: https://github.com/reactjs/react-modal/blob/master/src/helpers/tabbable.js#L17

The problem is that offsetWidth and offsetHeight aren't enough to determine if the contents of a node are visible. You also have to consider the overflow mode.

271 was meant to resolve this, though that was a year ago and might need updated... Would it be helpful if I rebased that commit or provided a demonstration of the issue?

@diasbruno I just tried the latest version of react-modal (3.1.2) and it doesn't work.

@conlanpatrek thanks! It sounds like that might be the fix.

@mandazi @conlanpatrek Sounds like a plan. Once it is rebased, we can merge it. (#271)

@conlanpatrek the PR #271 fixes the tab issue. Just verified!

271 has been rebased and updated to meet style standards.

Released v3.1.3.

I had the same issue today (2021), so I created a quick work around. All you need to do is create a ref an assign it to the container element that surrounds the focusable elements that you wish to tab through. For example:

import React, { useRef} from 'react';
import ModalTabManager from '../../yourpath';

const YourComponent = (props) =>{
const containerRef = useRef();
return (
<ModalTabManager containerRef={containerRef}>
    <form ref={containerRef}>
        <input type="text" />
        <input type="text" />
        <input type="text" />
        <input type="text" />
    </form>
 </ModalTabManager> 
);
}

Cut and paste the ModalTabManager component below into your project, do what I did in the example above, and that should fix your tabbing issues.

import React, { useState, useEffect } from 'react';

const ModalTabManager = ({ containerRef, children }) => {
    const [configuredTabIndexes, setConfiguredTabIndexes] = useState(false);

    const focusableElements = () => {
        return [...containerRef?.current?.querySelectorAll(
            'a, button, input, textarea, select, details, [tabindex]:not([tabindex="-1"]):not([type="hidden"]):not([disabled])'
        )];
    }

    const isTabbable = (element) =>{
        if(element.getAttribute('tabindex')){
            return true;
        }
        return false;
    }

    const findElementByTabIndex = (tabIndex) => {
        return containerRef?.current?.querySelector(`[tabindex="${tabIndex}"]`);
    }

    const moveFocusToTabIndex = (tabIndex) => {
        findElementByTabIndex(tabIndex)?.focus();
    }

    const handleFocusEvent = (event) => {
        if(!isTabbable(event.target)){
            return;
        }

        const tabIndex = parseInt(event.target.getAttribute('tabindex'));

        if(event.shiftKey && event.key === 'Tab'){
            moveFocusToTabIndex(tabIndex - 1);
        }else if(event.key === 'Tab'){ //should probably make sure there is no other modifier key pressed.
            moveFocusToTabIndex(tabIndex + 1);
        }
    }

    useEffect(() => {
        if(!configuredTabIndexes && containerRef.current){
            setConfiguredTabIndexes(true);
            focusableElements().forEach((el, index) => el.setAttribute('tabindex', index + 1));
            containerRef?.current?.addEventListener('keydown', event => {
                handleFocusEvent(event);
            });
        }
    });

    return children;
}

export default ModalTabManager;
Was this page helpful?
0 / 5 - 0 ratings

Related issues

yaoyao1024 picture yaoyao1024  路  3Comments

sizeight picture sizeight  路  3Comments

gavmck picture gavmck  路  3Comments

shaun-sweet picture shaun-sweet  路  3Comments

davidmfoley picture davidmfoley  路  3Comments