React Dropdown 可访问性 onClick

React Dropdown acessibility onClick

目前,此代码有效,但是当用户单击隐藏菜单时,useClickOutside 也会触发,菜单会关闭并再次打开...有什么办法可以解决这个问题所以当点击外面它关闭但是当点击按钮时它切换 on/off ?

const useClickOutside = (ref, handler) => {
    useEffect(() => {
        const clickHandler = (event) => {
            if (!ref.current || ref.current.contains(event.target)) {
                return;
            }
            handler(event);
        };

        document.addEventListener('mousedown', clickHandler);

        return () => {
            document.removeEventListener('mousedown', clickHandler);
        };
    });
};

const Settings = () => {
    const ref = useRef();
    const [toggle, setToggle] = useState(false);
    useClickOutside(ref, () => setToggle(false));

    return (
        <div className='settings'>
            <button onClick={() => setToggle(!toggle)} className='settings__button'>
                Menu
            </button>
            {toggle && (
                <div ref={ref} className='settings__panel'>
                    <Link className='settings__links' to='/user/settings'>
                        Your Profile
                    </Link>
                    <Link className='settings__links' to='/user/settings'>
                        Todos history
                    </Link>
                    <Link className='settings__links' to='/user/settings'>
                        Settings
                    </Link>
                    <Link className='settings__links' value={'Logout'} to='/user/login'>
                        Logout
                    </Link>
                </div>
            )}
        </div>
    );
};

您可以利用活动 stopPropagation。将调用 event.stopPropagation() 添加到隐藏菜单的 onClick 处理函数。

<button 
  onClick={(e) => {
    e.stopPropagation();
    setToggle(!toggle);
  }}
  className='settings__button'
  >
   Menu
</button>

这将防止 onClick 事件向上冒泡到下一个事件侦听器,即您的 onClickOutside 侦听器。

更新:

这仅在您的事件侦听器正在侦听 onClick 事件时才有效。您的内联 onClick 事件侦听器将仅停止 click 类型事件的传播。

document.addEventListener('click', clickHandler);

return () => {
  document.removeEventListener('click', clickHandler);
};

您可以考虑在 .settings div 上添加一个 onBlur 事件,其中 tabIndex=0.

然后您可以捕获 div 的模糊并测试事件是否来自 div 内部。

const onBlur = (e: FocusEvent < HTMLElement > ) => {
  if (opened?) {
    const element = e.relatedTarget;

    if (element == null) {
      // dropdown was blured because window lost focused. probably close.
    } else if (element != e.currentTarget) {
      if (!e.currentTarget.contains(element as Node)) {
        // blured element is not in .settings. close
      }
    }
  }
};

如果你想要花哨的,你也可以添加一个 keydown 并在逃跑时关闭。

const onKeyDown = (e: KeyboardEvent) => {
  if (e.key === "Escape") {
    // close!
  }
);

这是实现这些项目的 code sandbox