iT邦幫忙

2021 iThome 鐵人賽

DAY 4
1
Modern Web

React 從 0.5 到 1系列 第 4

[鐵人賽 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 毫無頭緒,不妨安裝這個套件開始,從提示反查去了解各項規範。


上一篇
[鐵人賽 Day03] 如何提升你的 React 網站易用性?(Web Accessibility)(中)- Accessible name、Keyboard Accessibility
下一篇
[鐵人賽 Day05] React 中的 Code splitting(代碼分離)方法
系列文
React 從 0.5 到 115

尚未有邦友留言

立即登入留言