[譯]React高級指引1:無障礙

原文連接:reactjs.org/docs/access…html

爲何要使用無障礙輔助功能

網絡無障礙輔助功能(也成爲a11y)可以被任何人使用的網站功能。無障礙輔助功能是容許輔助性技術解釋網站所必需的。react

React經過使用標準HTML技術支持構建無障礙輔助網站。ios

標準和指南

WCAG

網絡內容無障礙指南( Web Content Accessibility Guidelines ) 爲構建無障礙網站提供了指南。git

如下清單概述了WCAG的內容:github

WAI-ARIA

網絡無障礙協議-無障礙互聯網應用(Web Accessibility Initiative - Accessible Rich Internet Applications)包含了構建無障礙JavaScript部件所需的技術。web

全部以aria-開頭的HTML屬性在JSX中都是支持的。然而React中大部分DOM屬性都是小駝峯命名格式的(camelCased),可是aria-*則仍是按照HTML中的帶連字符的命名格式(也稱爲hyphen-cased、 kebab-case、lisp-case等):chrome

<input
  type="text"
  aria-label={labelText}
  aria-required="true"
  onChange={onchangeHandler}
  value={inputValue}
  name="name"
/>
複製代碼

語義化的HTML

在Web應用中,語義化的HTML是無障礙輔助功能的基礎。使用多種HTML來強化網站中的信息一般會讓用戶直接得到無障礙輔助功能。編程

有時候咱們爲了符合React的語法在JSX中加入<div>元素,但這破壞了HTML的語義,這點在使用列表(<ol><ul><dl>)或者<table>時更加突出。在這些場景下咱們應該使用React Fragment來組合其餘元素。設計模式

實例:數組

import React, { Fragment } from 'react';

function ListItem({ item }) {
  return (
    <Fragment>
      <dt>{item.term}</dt>
      <dd>{item.description}</dd>
    </Fragment>
  );
}

function Glossary(props) {
  return (
    <dl>
      {props.items.map(item => (
        <ListItem item={item} key={item.id} />
      ))}
    </dl>
  );
}
複製代碼

和其餘元素同樣,你能夠把把元素映射到fragement數組中。

function Glossary(props) {
  return (
    <dl>
      {props.items.map(item => (
        // Fragments should also have a `key` prop when mapping collections
        <Fragment key={item.id}>
          <dt>{item.term}</dt>
          <dd>{item.description}</dd>
        </Fragment>
      ))}
    </dl>
  );
}
複製代碼

若是你的開發工具支持且不須要使用props,你可使用最短語法:

function ListItem({ item }) {
  return (
    <>
      <dt>{item.term}</dt>
      <dd>{item.description}</dd>
    </>
  );
}
複製代碼

想要了解更多,請查看Fragment

無障礙表單

標籤

每個表單控制,例如<input>,<textarea>,都須要被標記來使用無障礙輔助功能。

下列資源告訴咱們如何標註元素:

儘管這些HTML實踐能夠直接在React中使用,可是for屬性在JSX中須要改寫成htmlFor

<label htmlFor="namedInput">Name:</label>
<input id="namedInput" type="text" name="name"/>
複製代碼

向用戶提醒錯誤

錯誤場景須要被全部用戶理解。下面的連接向咱們展現瞭如何向用戶展現錯誤文本:

控制焦點

確保你的web應用能夠徹底使用鍵盤操做:

鍵盤焦點及焦點輪廓

鍵盤焦點的定義是當前DOM中被選中的元素,用於接收用戶從鍵盤輸入的信息。它的焦點輪廓與下圖展現的相似。

若是你想要其餘的焦點輪廓樣式,惟一的辦法是使用CSS調整,好比使用outline:0

跳過內容的機制

爲了幫助和提速鍵盤導航,咱們提供了一種機制,它容許用戶跳過應用中的導航部分。

跳轉連接(Skiplinks)或者說跳轉導航連接(Skip Navigation Links)是隱藏的導航連接,只有使用鍵盤導航時他們纔可見。使用內部頁面錨點和一些樣式是很容易就能夠實現他們的。

固然咱們可使用地標元素或者角色(如<main><aside>)做爲輔助技術來劃分頁面區域,使得用戶能夠快速地導航到該區域。

閱讀下面的文章來提升對這些元素的認知:

以編程方式管理焦點

React應用在運行過程當中會不斷地修改HTML DOM,這有時候就致使了鍵盤焦點的丟失或者是出現了意料以外的元素。爲了修復這個問題,咱們須要在正確的方向手動地觸發鍵盤焦點。好比在點擊按鈕打開一個模式窗口後爲這個按鈕從新設置鍵盤焦點。

MDN Web 文檔關注了這個問題,並向咱們說明了如何創建鍵盤導航JavaScript部件

咱們可使用DOM元素中的Refs在React中設置焦點。

使用Refs,首先咱們在class組件中的JSX中爲元素添加ref:

class CustomTextInput extends React.Component {
  constructor(props) {
    super(props);
    // 建立ref來存儲input輸入元素
    this.textInput = React.createRef();
  }
  render() {
  //使用ref回調來存儲input元素的引用
    return (
      <input
        type="text"
        ref={this.textInput}
      />
    );
  }
}
複製代碼

以後咱們就能夠在組件的任意位置聚焦了。

focus() {
  //顯式地調用focus方法
  // 注意:咱們經過訪問current來得到DOM節點
  this.textInput.current.focus();
}
複製代碼

有時候須要父組件在子組件的元素上設置焦點,咱們能夠經過將refs暴露給父組件來完成此操做:將父組件的ref經過子組件中特殊的prop轉移到子組件的DOM中。

function CustomTextInput(props) {
  return (
    <div>
      <input ref={props.inputRef} />
    </div>
  );
}

class Parent extends React.Component {
  constructor(props) {
    super(props);
    this.inputElement = React.createRef();
  }
  render() {
    return (
      <CustomTextInput inputRef={this.inputElement} />
    );
  }
}

// 如今你能夠在須要時調用focus了
this.inputElement.current.focus();
複製代碼

當使用HOC擴展組件時,推薦使用React的forwardRef函數轉發到被包裹的組件中。若是第三方HOC不支持轉發ref,上面的模式也可以做爲回調函數使用。

一個好用的焦點管理例子:react-aria-modal。這是一個至關傑出的無障礙輔助功能完善的模式窗口案例。不只僅是由於它在取消按鈕上設置了初始焦點(防止用戶之外觸發成功按鈕)和把焦點固定在窗口以內,它也會在關閉窗口時將焦點設置到打開窗口的那個元素上。

注意 儘管這是無障礙輔助功能的一個很是重要的特性,但在使用時須要謹慎當心。咱們只須要在鍵盤焦點被打亂時使用它來修復,須要去嘗試預測用戶的行爲。

鼠標和指針事件

確保全部鼠標和指針事件暴露的功能使用鍵盤事件也能夠辦到。只依靠指針會致使在許多狀況下鍵盤用戶沒法使用應用。

爲了說明這個,咱們來看下由點擊事件破壞無障礙輔助功能的典型實例:外部點擊模式。用戶能夠經過點擊元素外部來關閉彈出框。

一般實現這個功能的方法是在 window對象上綁定點擊事件來關閉彈窗:

class OuterClickExample extends React.Component {
  constructor(props) {
    super(props);

    this.state = { isOpen: false };
    this.toggleContainer = React.createRef();

    this.onClickHandler = this.onClickHandler.bind(this);
    this.onClickOutsideHandler = this.onClickOutsideHandler.bind(this);
  }

  componentDidMount() {
    window.addEventListener('click', this.onClickOutsideHandler);
  }

  componentWillUnmount() {
    window.removeEventListener('click', this.onClickOutsideHandler);
  }

  onClickHandler() {
    this.setState(currentState => ({
      isOpen: !currentState.isOpen
    }));
  }

  onClickOutsideHandler(event) {
    if (this.state.isOpen && !this.toggleContainer.current.contains(event.target)) {
      this.setState({ isOpen: false });
    }
  }

  render() {
    return (
      <div ref={this.toggleContainer}>
        <button onClick={this.onClickHandler}>Select an option</button>
        {this.state.isOpen && (
          <ul>
            <li>Option 1</li>
            <li>Option 2</li>
            <li>Option 3</li>
          </ul>
        )}
      </div>
    );
  }
}
複製代碼

對於指針設備好比鼠標來講這樣作沒問題。可是使用鍵盤進行此操做時,由於window對象只接收點擊事件,用戶沒法tab到下一個元素,這會致使應用功能不完善,下降用戶體驗。

一樣的功能能夠經過使用合適的事件處理權柄來實現,好比 onBluronFocus:

class BlurExample extends React.Component {
  constructor(props) {
    super(props);

    this.state = { isOpen: false };
    this.timeOutId = null;

    this.onClickHandler = this.onClickHandler.bind(this);
    this.onBlurHandler = this.onBlurHandler.bind(this);
    this.onFocusHandler = this.onFocusHandler.bind(this);
  }

  onClickHandler() {
    this.setState(currentState => ({
      isOpen: !currentState.isOpen
    }));
  }
   //咱們經過使用setTimeout關閉彈出框
   //這是必要的,由於失焦事件在焦點事件前觸發,
   //咱們須要這個步驟確認這個元素的子節點是否得到焦點
  onBlurHandler() {
    this.timeOutId = setTimeout(() => {
      this.setState({
        isOpen: false
      });
    });
  }
	//若是子元素得到了焦點,就不關閉彈出框
  onFocusHandler() {
    clearTimeout(this.timeOutId);
  }

  render() {
  	//React經過把失焦事件和焦點事件傳遞給父元素幫助咱們
    return (
      <div onBlur={this.onBlurHandler}
           onFocus={this.onFocusHandler}>
        <button onClick={this.onClickHandler}
                aria-haspopup="true"
                aria-expanded={this.state.isOpen}>
          Select an option
        </button>
        {this.state.isOpen && (
          <ul>
            <li>Option 1</li>
            <li>Option 2</li>
            <li>Option 3</li>
          </ul>
        )}
      </div>
    );
  }
}
複製代碼

上述代碼把可用功能同時暴露給了指針設備和鍵盤用戶。注意代碼中添加的aria-*屬性是用來支持屏幕朗讀器lang'du'qilang'du'qlang'dulang'dlanglanlal的。爲了簡單起見,咱們這裏沒有實現用箭頭鍵交互彈出框的功能。

以上只是只依靠指針和鼠標事件破壞鍵盤用戶使用功能的例子之一。因此咱們須要常用鍵盤測試以定位問題所在區域,這樣咱們才能經過鍵盤感知程序來修復它。

更復雜的部件

複雜的用戶體驗並不表明它不是易於操做的。經過儘量接近HTML編程,無障礙訪問會變得更容易,甚至複雜的部件也能夠經過編碼輕易完成。

在這裏咱們要求掌握ARIA RolesARIA States and Properties。其中包含了許多HTML屬性的工具箱。這些HTML屬性能被JSX徹底支持,使用它們咱們能夠構建無障礙,功能強大的React組件。

每個部件都有各自特定的設計模式,而且用戶和用戶代理都指望能以類似的方式運行它們。

其餘須要考慮的點

設置語言

爲了屏幕朗讀器可以正確使用語音設置,請在頁面上正確設置人類語言。

設置文章標題

爲了使用戶清晰地記住文章的內容,請爲文章設置合適的<title>來描述文章內容。

咱們可使用React文章標題組件在React中設置文章標題。

色彩對比度

爲使有視覺障礙的用戶能清晰地閱讀網站上的文字,請確保它們有足夠的色彩對比度。

在網站中計算全部的顏色組合是一件很是無聊的事情,因此你能夠經過Colorable來計算一個徹底無障礙的調色板

在下面提到的aXe和WAVE工具都包含了色彩對比測試,而且會提供對比錯誤提示。

若是你想擴展你的對比測試能力,你可使用如下工具:

開發與測試工具

在構建無障礙網站的過程當中咱們可使用許多工具。

鍵盤

到目前爲止,最簡單也是最重要的檢查是測試你的網站可否單獨使用鍵盤操做。操做步驟以下:

  1. 移除你的鼠標
  2. 使用TabShift+Tab瀏覽
  3. 使用enter激活元素
  4. 須要時,使用箭頭鍵與元素進行交互,好比下拉列表或者菜單

開發輔助

某些無障礙特性咱們能夠直接在JSX中檢驗。一般支持JSX的IDE會對ARIA角色,state和屬性進行智能感知。咱們也可使用下列工具:

eslint-plugin-jsx-a11y

ESLint中的eslint-plugin-jsx-a11y 插件爲你在JSX中的無障礙功能提供了AST語法檢測回調。許多IDE容許你直接集成這些反饋到代碼分析和源文件窗口。

Create React App集成了這個插件的部分激活規則子集。若是你想要集成更多的無障礙規則,你能夠在項目更目錄建立.eslintrc文件,文件內容以下:

{
  "extends": ["react-app", "plugin:jsx-a11y/recommended"],
  "plugins": ["jsx-a11y"]
}
複製代碼

在瀏覽器測試無障礙輔助功能

不少工具能夠在瀏覽器上的網頁進行無障礙功能檢驗。由於它們只能測試你的HTML的技術無障礙行,因此請結合使用下面提到無障礙檢測工具使用。

aXe, aXe-core and react-axe

Deque系統提供的aXe-core可以爲你的應用提供端到端的自動無障礙檢測。這個模塊包含了對Selenium的集成。

無障礙引擎或aXe,是根據aXe-core構建的無障礙檢測瀏覽器插件。

你也能夠在開發或者debug時使用react-axe模塊將無障礙訪問的發現打印在控制檯上。

WebAIM WAVE

網絡無障礙評估工具也是一個無障礙輔助的瀏覽器插件。

無障礙輔助檢測器和無障礙輔助樹

無障礙輔助樹是DOM樹的子集,它包含應該暴露給輔助技術(如屏幕朗讀器)的DOM元素的全部無障礙輔助對象。

在某些瀏覽器中咱們能夠經過無障礙輔助樹來查看每個元素的無障礙輔助信息:

屏幕朗讀器

測試屏幕朗讀器應該稱爲你的無障礙輔助功能測試的一部分。

請注意瀏覽器和屏幕朗讀器的組合很重要。這裏推薦在瀏覽器中測試應用時選擇適合的屏幕朗讀器。

經常使用的屏幕朗讀器

Firefox的NVDA

NVDA(NonVisual Desktop Access)是普遍使用的開源屏幕朗讀器。

如何使用NVDA,請參考下列文獻:

Safari的VoiceOver

VoiceOver是蘋果設備自帶的屏幕朗讀器。

如何激活和使用VoiceOver請參考下列文獻:

IE的JAWS

JAWS(Job Access With Speech )是在Windows操做系統中經常使用的屏幕朗讀器。

如何使用JAWS請參考下列文獻:

其餘屏幕朗讀器

谷歌Chrome的ChromeVox

ChromeVox是Chromebook自帶的屏幕朗讀器,同時也是Google Chrome的一個插件。

如何使用ChromeVox請參考下列文獻:

相關文章
相關標籤/搜索