Tabindex 在 Chrome 中不起作用(React 应用程序)

Tabindex not working in Chrome (React app)

我使用 react 和 react-modal 在网站上创建覆盖。该叠加层包含各种元素以及一个表单(下面的概述)。我希望能够使用 TAB 键引导用户浏览表单。我将 tabindex=0 分配给了按出现顺序可标记的所需元素。

我的问题是:它在 Chrome(版本 61.0.3163.100)中不起作用,而在 Firefox 中却可以。我读到,如果 DOM-树上的任何元素不可见或 height/width 为 0,就会发生这种情况。我进行了一些样式更改以修复该问题,但没有效果。

<div class="ReactModalPortal">
<div data-reactroot="" class="ReactModal__Overlay" style="position: fixed; top: 0px; left: 0px; right: 0px; bottom: 0px;">
    <div class="ReactModal__Content" tabindex="-1" aria-label="Questionnaire" style="position: absolute; top: 0px; left: 0px; right: 0px; height: 100%; background: transparent none repeat scroll 0% 0%; overflow: auto;">
        <!-- Some other stuff and nested elements -->
        <div id="...">
            <form>
                <input tabindex="0">
                <button tabindex="0">
            </form> 
        </div>   
    </div>   
</div>    

如您所见,其中一个父元素具有 tabindex="-1"。当通过 Chrome 中的检查功能或以编程方式使用 JS 更改它时,问题仍然存在(或者如果元素最初是使用此索引呈现的,是否有所不同?)。

更新

我意识到是其他原因导致了问题。我在模态的根节点上使用 CSS 属性 initial: all 来将我的内部 CSS 与外部的所有内容隔离开来。由于某种原因,这阻止了 tabindex 工作。如果你能帮助我理解,我会奖励这个赏金。我的解决方法就是不使用 all: initial(它无论如何都不兼容 IE,但我也没有真正好的替代方法)。

all: initial 重置具有初始属性的节点的所有 CSS 属性。

对于display 属性,初始值为inline

因此,将 all: initial 设置为根 div 会将 display 属性 设置为内联。 inline 元素没有高度或宽度,因此它们是 0x0。

这也是因为 div 只包含固定的、绝对定位的元素。

React Modal 通过 运行 遍历模态内所有元素的循环检查元素是否可聚焦。但是,对于可聚焦的元素,它必须是可见的。对于每个元素,我们必须迭代直到 body 元素以确保它的可见性。

这是检查元素是否可见的函数。

function hidden(el) {
  return (
    (el.offsetWidth <= 0 && el.offsetHeight <= 0) || el.style.display === "none"
  );
}

如您所见,我们的 div 将没有 offsetHeightoffsetWidth,因此将被视为隐藏。因此,模态不能不被聚焦。

我遇到了同样的问题,无法让其他解决方案快速运行,所以我想出了蛮力方法。对容器元素进行引用,该容器元素包含您希望使其成为可选项卡的可聚焦元素。

  const formRef = useRef();

  <ReactModalTabbing containerRef={formRef}>
        <form ref={formRef} onSubmit={handleSubmit}  >
            <input type="text" />
            <input type="text" />
            <input type="text" />
            <input type="text" />
        </form>
  </ReactModalTabbing>

这是组件

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

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

    const focusableElements = () => {
        // found this method body here.
        //https://zellwk.com/blog/keyboard-focusable-elements/
        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 handleKeyDownEvent = (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', handleKeyDownEvent);
        }
    });

    return children;
}

export default ReactModalTabbing;