键盘可访问的 React 组件可在悬停和焦点时安装?

问题描述 投票:0回答:1

在 React 应用程序中,我有一个由

<Card />
组件组成的网格。每个
<Card />
渲染多个
<Button />
组件。

由于我无法控制的限制,

Button
是一个低效的组件。渲染成本很高,而且我们需要渲染数百个
<Card />
组件,因此它成为性能问题。

考虑到这一点,我只想在用户主动与

<Button />
 交互时安装/渲染 
<Card />

  • 当用户将鼠标悬停在
    <Card />
    上时,安装
    <Button />
  • 当用户通过键盘导航到
    <Card />
    时,安装并聚焦
    <Button />
  • 允许键盘用户在
    <Card />
    组件之间完全向前/向后循环

悬停部分相当简单,我的实现如下所示。

我的键盘部分遇到问题。由于

<Button />
尚未安装,用户键盘如何导航到它?

我尝试过的CodeSandbox链接

大部分都有效,除了两个重要的方式:

  1. <Button />
    被插入时,第一个
    <Card/>
    应该立即获得焦点(不需要按第二次 Tab 键)
  2. 反向键盘导航(即shift+tab)不允许任何
    <Button>
    获得焦点。它应该聚焦于“最后一个”按钮,并允许用户导航所有按钮,然后转到上一个
    <Card>

问题:我必须对下面的代码进行哪些修改才能支持以上两点?

import { useState, Fragment, useEffect, useRef, MutableRefObject } from "react";
import Button from "./Button";

export default function Card() {
  const [isHovered, setIsHovered] = useState<boolean>(false);
  const [showButtons, setShowButtons] = useState<boolean>(false);
  const containerRef: MutableRefObject<HTMLDivElement | null> = useRef(null);

  useEffect(() => {
    setShowButtons(isHovered);
  }, [isHovered]);

  return (
    <div
      onMouseOver={() => setIsHovered(true)}
      onMouseLeave={() => setIsHovered(false)}
      onFocus={(event) => {
        event.persist();
        setIsHovered(true);
      }}
      onBlur={(event) => {
        event.persist();

        window.requestAnimationFrame(() => {
          if (containerRef.current) {
            setIsHovered(
              containerRef.current.contains(document.activeElement) === true
            );
          }
        });
      }}
      tabIndex={0}
    >
      <div ref={containerRef}>
        <h2>Card</h2>
        {showButtons && (
          <Fragment>
            <Button>Expensive Button One</Button>
            <Button>Expensive Button Two</Button>
          </Fragment>
        )}
      </div>
    </div>
  );
}
javascript reactjs accessibility onblur onfocus
1个回答
0
投票

您可以使用简单的 CSS(

opacity
属性)和焦点状态更改来达到所需的结果。

请注意,使非交互式卡片容器组件与

tabIndex={0}
交互是一种反模式。

//styles.css
.invisible-button {
opacity:0;
}


//Button.tsx
import React, { useState, FC } from "react";
export const Button: FC<{ children: React.ReactNode }> = ({
  children,
    }) => {
  const [hasBeenFocused, setHasBeenFocused] = useState(false);
  const handleFocus = () => setHasBeenFocused(true);
  const handleBlur = () => setHasBeenFocused(false);
  return (
    <button
      className={hasBeenFocused ? "" : "invisible-button"}
      onFocus={handleFocus}
      onBlur={handleBlur}
      onMouseEnter={handleFocus}
      onMouseLeave={handleBlur}        
    >
      {children}
    </button>
  );
};
export default Button;

看看完整的沙箱: https://codesandbox.io/p/sandbox/intelligent-matsumoto-frc9kk?file=%2Fsrc%2FApp.js%3A14%2C1

如果您希望仅让按钮获得焦点,则无需使卡片组件与 tabIndex 属性交互。

我希望我的解决方案有帮助:-)

© www.soinside.com 2019 - 2024. All rights reserved.