2. ARIA 在 HTML 中的使用说明

2.1 ARIA 使用第一原则

如果你可以使用具有所需语义和行为的原生 HTML 元素 [HTML51] 或属性(已内建),而不是重新定义元素并添加 ARIA 角色、状态或属性使其可访问,则应优先使用原生元素

在什么情况下可能无法实现?

2.2 ARIA 使用第二原则

不要更改原生语义,除非你确实必须这样做。

例如:开发者想做一个是 Tab 的标题。

不要这样做:

<h2 role=tab>heading tab</h2>     

应该这样做:

<div role=tab><h2>heading tab</h2></div>

如果用非交互式元素来做交互式组件,开发者必须用 ARIA 增加语义,并通过脚本实现交互行为。比如想要实现一个按钮,实际上用原生 HTML 按钮更好更简单

可以使用具有与 ARIA 角色相似语义的原生 HTML 元素做降级处理。例如,用 HTML 列表元素 作为带 ARIA树形控件的结构骨架。

2.3 ARIA 使用第三原则

所有交互式 ARIA 控件必须支持键盘操作。

如果你创建的组件可通过点击、触摸、拖拽、滑动或滚动进行操作,用户也必须可以通过键盘导航并进行等效操作。

所有交互式组件都必须用脚本支持标准按键或按键组合。

例如,使用 role=button 时,元素必须可以获得焦点,且用户必须同时可以通过 enter(WIN)或 return(MAC)以及 space 键激活对应操作。

可参阅 [wai-aria-practices-1.1] 文档中的 设计模式与组件键盘接口开发 章节。

2.4 ARIA 使用第四原则

不要在可聚焦元素上使用 role="presentation"aria-hidden="true"

这些属性用于可聚焦元素,会导致部分用户聚焦到“空处”。

不要这样做:

<button role=presentation>press me</button>   

不要这样做:

<button aria-hidden="true">press me</button>   

给可见交互元素的父元素或祖先元素添加 aria-hidden 也会让其“连带”被隐藏,同样不要这样做


<div aria-hidden="true"> 
<button>press me</button>
</div>

如果交互式元素不可见无法操作,且不可聚焦,可以添加 aria-hidden。例如:

 button {opacity:0}

<button tabindex="-1" aria-hidden="true">press me</button>

如果使用 display:nonevisibility:hidden 隐藏交互元素(自身或祖先),它不会被聚焦,也会从可访问性树中移除,这种情况下再加 aria-hidden="true"tabindex="-1" 就没必要了。

2.5 ARIA 使用第五原则

所有交互式元素必须有可访问名称

交互式元素只有在其可访问性 APIaccessible name(或等效)属性有值时,才真正有可访问名称。

例如,下方代码的 input type=text 有“user name”标签,但没有可访问名称:


    user name <input type="text">

    or

    <span>user name</span> <input type="text">
    

控件的 MSAA accName 为空:

示例控件,MSAA 的 accName 没有值,accRole 是 'editable text'。

而下方代码的 input type=text 用“user name”做标签,也有可访问名称。因为 input可加 label 的元素,而 label 元素正确将标签内容和输入框关联。


<!-- 注意: for/id 或嵌套 label 可以让控件有可访问名称 -->

<input type="text" aria-label="User Name">

or

<span id="p1">user name</span> <input type="text" aria-labelledby="p1">

控件的 MSAA accName 是 "user name":

示例控件,MSAA 的 accName 是 'user name',accRole 是 'editable text'。

注:上例针对 ARIA 组件。普通 HTML 输入,建议优先用第一原则,label 元素用 forinput 关联标签。

HTML label 元素和可加 label 元素

以下关于在 HTML 中用 label。如在做 ARIA 组件,请参阅 ARIA 编写实践文档

label 元素不能为自定义控件提供可访问名称,除非引用的是原生可加 label 元素


     <!-- HTML input 加 combox 角色 -->

      <label>
      user name <input type="text" role="combobox">
      </label>
    

控件的 MSAA accName 是 "user name":

示例 input,MSAA accName='user name',accRole='combo box'。

不管加什么 role,div 都不是 HTML 的可加 label 元素


     <!-- HTML div 加 combox 角色 -->

      <label>
      user name <div  role="combobox"></div>
      </label>
    

控件的 MSAA accName 为空:

示例 div,MSAA accName 为空,accRole='combo box'。

第五原则内容补充完善中

 

2.6 添加 Role 对原生语义的影响

添加 ARIA 角色会覆盖元素在 无障碍树(accessibility tree) 中的原生语义,该树通过无障碍API暴露给外部,这样 ARIA 就间接影响了读屏软件或其他辅助技术获取的内容。

比如,HTML 结构里的代码如下:

<h1 role=button>text</h1>

在无障碍树中变为:

button  with a label of 'heading text'

添加 Role 不会带来的效果

添加 ARIA 角色并不会让元素在不使用辅助技术的用户看来有任何变化。它不会改变宿主元素的行为、状态或属性,而只是改变原生语义。

比如,HTML 结构里的代码:

<button role=heading aria-level=1>text</button>

在无障碍树中变为:

a heading

但是此元素仍然可以被点击,仍然处于默认的 Tab 顺序,依然展示为按钮的样式,被按下时还会触发关联事件。所以 HTML5 不允许用 Role 把 button 变成 heading,是不合规范的做法

注意:改变元素 role不会给该 role 增加行为、属性或状态。ARIA 也不会改变它在浏览器中的外观与行为。例如,让链接表现为按钮,仅仅添加 role=button 是不够的,还需通过脚本加入键盘事件处理,比如监听 space 键,因为原生按钮可以用 enterspacebar 激活。

2.7 ARIA 应内嵌还是通过脚本动态添加?

如果 ARIA 角色或 aria-* 属性不需要脚本实现交互行为,那么可以直接在 HTML 中内嵌 ARIA 标记。比如添加ARIA 标志性角色、aria-label/aria-labelledby/aria-describedby 属性。

如果内容和交互只有在支持脚本的浏览环境下才可用(如Google docs 这类应用依赖 JavaScript),那么把 ARIA 内嵌进 HTML 也是可以的——因为无 JS 就什么都不能用了。

其他情况,建议用脚本动态插入、改变、移除 ARIA。比如树控件下折叠项代码是:

<li role=treeitem aria-expanded=false ...

当用户展开时用 JS 改为:

<li role=treeitem aria-expanded=true ...

2.8 ARIA 校验

最简单的方法是用HTML5 的 DOCTYPE,直接写 ARIA,再用W3C Nu Markup Checker 检查。ARIA 其实和其他 DOCTYPE 也兼容,但老的校验工具检测到 ARIA 时可能报错,因为他们的 DTD 没有也不会升级支持 ARIA。

HTML5 之前的版本校验 ARIA 标记报错,其实对实际可访问性和用户体验没有负面影响。这些只是老校验工具没考虑 ARIA 而已。

注意:W3C Nu Markup Checker ARIA 校验功能还在完善中,不能完全依赖(但已经很不错了)。如遇结果与 ARIA/HTML 规范冲突,建议提交 issue

2.9 Role=presentation 或 Role=none 的用法

role=presentation 或同义 role=none,会把其所在元素的语义删除。

比如,HTML 结构如下:

<h1 role="presentation">text</h1>

无障碍树中:

text, no heading

也就是说,只会当作无语义的纯文本。

对于无必需子元素的元素,role=presentation/none 不会影响其内部子元素的语义。

比如如下代码:

<h1 role="presentation"><abbr>API</abbr></h1>

无障碍树中:

abbr with text of API

但对于拥有必需子元素(如 ultable)的元素,role=presentation/none 除了自身语义被移除,其内部所有必需子元素也会失去语义。

比如代码:

<table role="presentation">
  <tr>
    <td>
      <abbr>API</abbr>
    </td>
  </tr>
</table>

无障碍树中:

abbr with text of API

注意:任何非必需子元素不会受 role=presentation/none 影响,包括嵌套的 list 或 table 等。

例如下列嵌套表格:

<table>
 <tr>
  <td>
    <table>
     <tr>
      <td>
       <abbr>API</abbr>
      </td>
     </tr>
    </table>
  </td>
 </tr>
</table>

无障碍树中:

outer ttable with 1 row and 1 cell containg another table with 1 row and 1 cell containing an abbr element.

如果给最外层 tablerole=presentation/none

<table role="presentation">
 <tr>
  <td>
    <table>
     <tr>
      <td>
       <abbr>API</abbr>
      </td>
     </tr>
    </table>
  </td>
 </tr>
</table>

无障碍树中,外层 table 及其所有必需子元素(trtd)语义被移除:

table with 1 row and 1 cell containing an abbr element

role=presentation/none 示例

用于修正错误表格结构:

<div aria-readonly="true" role="grid">
    <table role="presentation">
    <tbody><tr role="row">
    <th role="columnheader">Dog Names</th>
    <th role="columnheader">Cat Names</th>
    <th role="columnheader">Cow names</th>
    </tr>
    </tbody></table>
    <table role="none">
    <tbody><tr role="row">
    <td role="gridcell">Fido</td>
    <td role="gridcell">Whiskers</td>
    <td role="gridcell">Clarabella</td>
    </tr>
    <tr role="row">
    <td role="gridcell">Woofie</td>
    <td role="gridcell">Claws</td>
    <td role="gridcell">Glenn</td>
    </tr>
    </tbody></table>
    </div>

2.10 实用支持:aria-label、aria-labelledby 和 aria-describedby

以下是当前支持情况:

上述这些在 iframe 里也同样有效。aria-labelaria-labelledby 在读屏和无障碍 API 表现一样,只是 aria-label 通常留给没有可见文本参考或 id 管理困难的场景。

IE 关于 aria-labelledby、aria-label、aria-describedby 的备注

Internet Explorer 里,如果 aria-labelledby 用了多个 id,或 aria-describedby 用单/多个 id,被引用的元素必须是微软定义的可访问 HTML 元素

下面这个 aria-labelledby 多引用的例子,用了带 tabindex=-1span。详细看让非可访问元素变为可访问元素

<label id="l1" for="f3">label text</label>

<input type="text" id="f3" aria-labelledby="l1 l2" >

<p>other content</p>

<span tabindex="-1" id="l2" >more label text</span>

在 IE 里,有了 ARIA 角色也会使元素成为可访问 HTML 元素,如:

<div aria-describedby="test">text</div>

<div id="test" role="tooltip" >tooltip text</div>

隐藏内容不影响名称/描述计算

默认情况下,被 aria-labelledbyaria-describedby 引用的内容(即使用 CSS display:nonevisibility:hidden 或 HTML hidden 属性)仍会用于名称/描述计算。

默认情况下,辅助技术不会读隐藏内容,但开发者可以通过 aria-labelledbyaria-describedby 强制把隐藏文本当做可访问名称或描述的一部分。

下面例子,不管哪种状态辅助功能都能读取描述:

非错误状态:消息是视觉隐藏


<label>Name <input type="text"  aria-describedby="error-message"></label>
<span id="error-message" style="display:none">
You have provided an incorrect name</span>

注意:即便被引用元素加 aria-hidden=true 也没有影响:

<span id="error-message" style="display:none" aria-hidden="true">
 You have provided an incorrect name</span>

错误状态:消息显示

<span id="error-message" style="display:inline">
You have provided an incorrect name</span>

上下文名/描述文本的常见做法

如果你需要关联上下文相关的文本(如错误信息),可以:

2.10.1 可访问名称对背景图片的影响

避免用 CSS background 展示有信息意义的图片。如果图片对用户很重要应用 <img> 并加 alt。CSS 规范写到:

出于可访问性考虑,作者不应该只用 background-image 传递重要信息。见WCAG failure #F3 [WCAG20]。图片内容在非图形化界面不可访问,background images 也可能被高对比度模式关闭。参考

如果必须用 CSS 图片或需为"无重要性"气氛图设置替代文本怎么办?

CSS 规范之所以不是强烈禁止而是 推荐,是考虑到可能因为视觉设计/旧代码,无法不用 background-image。此外,开发者可能想给有气氛作用但不重要的图片(如背景照片)加上替代文本,以方便部分有需要的用户浏览。这里有篇气氛 vs 装饰 vs 信息性图片的详细解读。

用 CSS 图片给替代文本的注意事项

如果 <div> 包含内容,而加了 role="img"aria-label,会导致辅助技术忽略内部内容或只显示 aria-label,因为有可访问名称计算规则

最好不要把有内容的 <div> 用作背景图片容器。推荐用空 <span>,加 role="img"aria-label

推荐写法:

<div>
<span class="background-image" role="img" aria-label="[place alt text here]"> </span>
[all the rest of my content]
</div>

不要这样:

<div class="background-image" role="img" aria-label="blah blah blah">
[all the rest of my content]
</div>

如确实要将 CSS 图片用在含内容 <div> 上怎么办

有时候 CSS 样式依赖很强,不能随意拆分,或提交需求很慢变更难。这时可以这样变通:

<div class="background-image" >
<span role="img" aria-label="[place alt text here]"> </span>
[all the rest of my content]
</div>

这种写法属于折中方案,因为语义上替代文本并没有加在实际有图片的元素上,但对读屏来说图片的那个 div 被略过,紧跟的 <span> 替代文本会让信息基本能传递给有需求的用户。

2.11 使用 ARIA role=application

role="application" 对屏幕阅读器的影响

在许多主流屏幕阅读器中,用户处于浏览模式时,大多数按键会被屏幕阅读器拦截,而不是传递给网页。这对于高效浏览页面很有必要。截至目前,如果设置了 application 模式,许多屏幕阅读器将不再拦截按键,而是把所有按键直接传递给浏览器。这样用户将无法像以前那样方便地浏览页面,例如不能通过标题快速跳转,或者按行朗读一段静态文本。不过,也有一些屏幕阅读器在设置了 application role 后并不会有不同的行为。

该什么时候使用,什么时候不该用?

是否使用 role=application 时,开发者需要权衡保留屏幕阅读器快捷键带来的便利与功能丧失的影响。通常情况下不应使用,如果要用,应与屏幕阅读器用户做可用性测试。

如果控件一组只含以下所有标准 HTML 部件,无论你是用原生 HTML 组件还是用 WAI-ARIA 角色做的,都不应该role="application"

注意:不建议开发自定义文本输入控件,几乎所有场合都应使用原生输入控件。

如果你的控件属于下列更动态、非原生部件,也不需要application,因为支持 WAI-ARIA 的屏幕阅读器默认会在这些场景下自动切换浏览/聚焦模式:

只有当你的内容只包含可聚焦交互控件,并且多数为模拟桌面应用的高级部件时,才可以考虑用 role=application。即便很多内容现在称为 Web 应用,其核心信息结构仍然是文档型,比如社交帖,博客,推文,手风琴等。Web 上的内容本质仍属“文档”,只是表面看起来像桌面应用。

用户在屏幕阅读器表单(聚焦)模式下使用控件时,不必用 role=application 也能获取专属快捷键。例如一个带 ARIA role=listbox 的自定义控件可以捕获所有按键(包括 方向键),用户操作时无障碍体验不受影响。

简言之:实际上需要用 role=application 的场景极为罕见

少数必须用 role=application 时应如何加?

加在包含控件最近的父元素上,比如最外层 widget 的父 div。如果外层 div 只包裹需要切换为“应用交互模型”的 widget,用户 tab 出控件后聚焦模式能切换回来。

只有当页面完全由需要聚焦模式的 widgets 组成,才应加在 body 元素上。如果大多数为 widget 但部分内容还是文档时,建议给页面上有关“文档区”最外层元素加 role=document 。这是 role=application 的对立面,让读屏用浏览模式聚焦文档区。同时最好设置 tabindex=0 让用户能聚焦。

经验法则:如果页面 90% 甚至 95% 都是 widget,才可能role=application。即便如此,也应该让有经验的人实测“加/不加 role=application”两种方案,选择最佳体验。

绝不要在 body 这类大容器上加 role=application,如果你的页面以传统控件、链接等内容为主,而这些控件不用聚焦模式。否则辅助技术用户会遇到极大困扰。

更多 role=application 用法详见 If you use the WAI-ARIA role "application", please do so wisely!

2.12 自定义控件可访问性开发检查表:

请根据下方设计考量点检查你的自定义控件。如果有任何一项答案为“No”,应在发布前修复,或至少记录相关限制以提醒其他开发者——你的控件因可访问性支持有限,部分用户将无法使用。

自定义控件设计考量点
设计考量点 说明 是/否
可聚焦 可以用键盘定位到该控件吗?参考 键盘聚焦支持
支持键盘操作 可以用键盘操作该控件吗?参考 键盘导航
支持触摸操作 可以用触摸手势操作控件吗?可辅以辅助技术?
标准操作方式 能否用标准按键(参考 ARIA 组件设计模式)或控件类型对应的手势进行操作?
焦点可见性明确 控件获得焦点时能清楚看到吗?参考 焦点可见性(WCAG2)
标签 该控件有文本标签,并且标签已作为 可访问名称暴露在 无障碍 API
角色 控件有合适的角色并暴露在 无障碍 API
状态和属性 控件的任意 UI 状态或属性暴露在 无障碍 API
色彩对比度 控件标签/描述/图标对于低视力用户可见可用(可用 色彩对比度分析工具
高对比模式 控件在启用高对比模式时依然可见可用(如 Windows 高对比模式

2.13 ARIA 对大多数 HTML 元素的默认语义无补充

在某些情况下,HTML 元素的语义可以通过 ARIA 的 role、state 或 property 表达。这被称为元素的‘默认隐式 ARIA 语义

HTML4 中定义的元素无需为其默认语义添加 ARIA role。添加 ARIA role 属于额外工作,无益处,甚至可能带来问题。HTML5 新定义的特性,大多数浏览器已自动暴露其默认语义。 HTML 规范中说明如下:
在大多数情况下,设置 ARIA role 及/或 aria-* 属性,并且其值与 默认隐式 ARIA 语义 匹配,是没必要且不推荐的,因为这些属性已由浏览器设置。

2.13.1 冗余 ARIA 示例

为 HTML5 Recommendation 列出的交互元素添加默认隐式 role 纯属浪费时间:

<button role="button">press me</button>   

为本身已有等效 HTML 属性同时添加 ARIA state 或 property 也是浪费时间:

<input type="text" required aria-required="true">

<div hidden aria-hidden="true">
给长期实现的结构性元素加 ARIA role 和 state/property 也是冗余的:
<h1 role="heading" aria-level="1">heading text</h1>

2.14 HTML 尚不支持的 ARIA 角色和属性

下面列出了被认为 HTML 原生不支持的 ARIA 角色和属性。很明显 ARIA 提供的许多可用于向用户传达信息的角色和属性,在 HTML 中尚未提供支持。

2.14.1 ARIA 角色

  1. alert
  2. alertdialog
  3. application
  4. directory
  5. document
  6. feed
  7. grid
  8. gridcell
  9. group
  10. log
  11. marquee
  12. menu
  13. menubar
  14. menuitemcheckbox
  15. menuitemradio
  16. none
  17. note
  18. presentation
  19. scrollbar
  20. search
  21. status
  22. switch
  23. tab
  24. tablist
  25. tabpanel
  26. timer
  27. toolbar
  28. tooltip
  29. tree
  30. treegrid
  31. treeitem

2.14.2 ARIA 状态和属性(aria-* 属性)

  1. aria-activedescendant
  2. aria-atomic
  3. aria-busy (状态)
  4. aria-controls
  5. aria-describedby
  6. aria-dropeffect
  7. aria-expanded (状态)
  8. aria-flowto
  9. aria-grabbed (状态)
  10. aria-haspopup
  11. aria-hidden (状态)
  12. aria-label
  13. aria-labelledby
  14. aria-level
  15. aria-live
  16. aria-orientation
  17. aria-owns
  18. aria-posinset
  19. aria-pressed (状态)
  20. aria-relevant
  21. aria-setsize
  22. aria-sort

2.15 ARIA 设计模式与触控设备支持

警告

ARIA 设计模式WAI-ARIA 编写实践 1.1 中描述了如何实现自定义 UI 元素,使其仅用键盘也能操作,并能被辅助技术用户理解。部分 ARIA 设计模式目前依赖于仅键盘事件处理。在只支持触摸屏的设备上,该模式是不支持的;而在配备物理键盘的手机/平板设备上,支持也有限或无支持(视系统而定)。有一个正在整理中的 ARIA 设计模式-触摸 UA/AT 差距分析(Google 表) 可供参考(.ods 静态文件版见这里)。相关 WAI-ARIA 编写实践 1.1 问题

2.16 推荐表:

可参考 HTML 中 ARIA 属性使用的文档合规性要求 表格,详见 ARIA in HTML 规范。

2.17 ARIA 角色、状态和属性速查表

可参考 允许的 ARIA 角色、状态和属性 表格,详见 ARIA in HTML 规范。