[铁人赛 Day04] 如何提升你的 React 网站易用性?(Web Accessibility)(下)- Mouse and pointer events、Development Tools

文章大纲与涵盖范围

继上篇介绍完无障碍网站(Web Accessibility,又称为 a11y)的目的与实践方向,中、下篇将着重在 React Advanced Guide 里提供的 Accessibility 指南。

在可以广泛运用的 Accessibility 实作知识上,hightlight React 对此的支援(例如 JSX 是否也能直接应用某些 HTML 属性?)或者调整(某些属性在 React 里,可能有写法的差异)。

下篇会使用一个案例,介绍滑鼠点击事件的实作,并且使用 React 生态系中的测试工具测试效果。

实作 Mouse and pointer events 的 Accessibility 优化

请想像一个需求:在画面上,有三个按钮,第一个按钮点击之後,会 pop up 出一个子目录列表,这个列表需要可以关掉,第二三个按钮点击之後,会跳出 alert 视窗(但在这个案例我们不用关注二跟三)。

你会怎麽设计这个元件呢?以下是一个很直观,但其实对於键盘使用者不太友善的做法,你是否也看出端倪了呢?原本的 React 文件使用 class component,这里我用 hook 稍加改写。

首先,让我们把元件上的按钮先画出来,并且设定好列表 pop up 的状态,再加上点击按钮之後会展开列表的函式。

export const ClickExample = () => {
  const [isShowOption, setIsShowOption] = useState(false)
	const onClickHandler = () => {
    setIsShowOption(true)
  }
  return (
    <>
      <div>
        <button onClick={onClickHandler}>Select an option</button>
        {isShowOption && (
          <ul>
            <li>Option 1</li>
            <li>Option 2</li>
            <li>Option 3</li>
          </ul>
        )}
      </div>
      <button>Another button</button>
      <button>The other button</button>
    </>
  )
}

加入了一些排版之後,此时的画面看起来:

pic

接下来,我们要加入一个点击空白处,就可以把列表关起来的方法。在这里新增了一个 ref ,并指向底下的子目录列表外层元素,让我们可以获得 current 的状态。当点击范围不在列表元素的 current 范围里的时候,就收合列表。

export const ClickExample = () => {
  //..
  const toggleContainer = useRef(null);
  useEffect(() => {
    const onClickOutsideHandler = (event) => {
      if (isShowOption && !toggleContainer.current.contains(event.target)) {
        setIsShowOption(false);
      }
    }
    window.addEventListener('click', onClickOutsideHandler);
    return () => {
      window.removeEventListener('click', onClickOutsideHandler);
    };
  });
  return (
    <Container>
      <div ref={toggleContainer}>
 //..
)}

此时的画面看起来:
pic-2

用滑鼠控制的情况下,还算顺利。然而当使用者用键盘时,就会遇到问题。(以下的画面键盘顺序为:使用 tab 切换到第一个按钮,按下 enter 打开子目录,接下来按 enter 或是 tab 都无法把子目录关上)。

clickEvent-3.mov

此时,可以利用别的 event handlers 事件来改善这个情境,拿掉点击空白处关闭列表的设定。可以设计成侦测使用者的滑鼠、Focus 的状态,如果已经离开了按钮一一段时间,就自动关上列表。如此一来,不管是键盘或是滑鼠的使用者,可以顺畅的操作画面。

所以在这里把原本的 ref 以及 event listener 拿掉,侦测按钮,当别的按钮被 focus 而按钮一的 focus 状态解除时,就把子目录列表关上。这里使用 setTimeout 的原因是:blur event 会比新的 focus event 更早触发,而我们必须要先去确认别的按钮是否被 focus 才能决定是否要关上列表。

export const ClickExample = () => {
  //..
  let timeOut
  const onBlurHandler = () => {
    timeOut = setTimeout(() => {
      setIsShowOption(false);
    })
  }
  const onFocusHandler = () => {
    clearTimeout(timeOut);
  }

  return (
		// React assists us by bubbling the blur and focus events to the parent.
    <Container>
      <div onBlur={onBlurHandler} onFocus={onFocusHandler}>
  //..
  )

}

经过调整之後,画面的互动看起来像是这样:

使用滑鼠

clickEvent-4.mov

使用键盘

clickEvent-5.mov

JSX 的无障碍网页 Development Tools

Crete React App 会自动装载 eslint-plugin-jsx-a11y 这个套件(所以 Create Next App 也是有的)。

https://ithelp.ithome.com.tw/upload/images/20210919/20140045PgZeZVu6ng.png

如果你想要引入更多的 accessibility 规则提示,可以调整你的 .eslintrc 档案。你可以到这里 https://github.com/jsx-eslint/eslint-plugin-jsx-a11y#supported-rules 来看看它支援的规则。

加上去之後,就可以看到相关提示了!例如下图是在说 <img> 元素必须有个 alt 属性来说明内容。

https://ithelp.ithome.com.tw/upload/images/20210919/20140045OPPD2qds87.png

这一张则是跟表单有关的设定。

https://ithelp.ithome.com.tw/upload/images/20210919/20140045P3pJaXvZrn.png

关於这些规则,套件里的文件也都写得相当清楚。

https://ithelp.ithome.com.tw/upload/images/20210919/20140045tC5fhG2aV2.png

如果觉得要改善自己的专案 Accessibility 毫无头绪,不妨安装这个套件开始,从提示反查去了解各项规范。


<<:  Day04 - 事件、状态转移

>>:  Ruby on Rails CRUD 之 U(Update)

Day 1:Native vs. Not-so-native

接下来的文章,我把范围限缩在「桌上型作业系统」,目前主流的作业系统有 Windows, macOS,...

[Day03] Medium

Medium 的特色 Medium 基本上是为了部落格而生的平台,简约的排版画面、所见即所得的编辑器...

Day15 简易资料库RealmSwift小实作2

继续昨天~ 拉取三个Label,这是往後会从资料库回传资料到我们在主画面拉的tableview里显示...

Day 28:我的部落格没有人气怎麽办?

嘛,铁人赛来到第 28 天,同时也宣告我的铁人赛库存没有了(剩下一篇写到一半的库存是 for 第 3...

[day27] - Angular Component to Web Component

後来发现 , 之前说明了 Vue . React Component 如何变成 Web Compon...