初探 WAI-ARIA

avatar
UX @京东

image

文章背景

近期开发的一个项目,运行ESLINT某行提示报错
image
出错行代码

<i className="eye-ico J_ping" onClick={this.toggleInputType}/>

jsx-a11y/no-static-element-interactions
查找文档出现关键词 WAI-ARIA

WAI-ARIA是什么?

WAI-ARIA (Web Accessibility Initiative-Accessible Rich Internet Applications),指无障碍网页应用。主要针对的是视觉缺陷、失聪、行动不便的残疾人以及假装残疾的测试人员。
比如盲人眼睛看不到,其浏览网页则需要借助辅助设备一般都是使用读屏软件。
常见的读屏软件有:
移动端:

  • Android: TalkBack
  • Android: Funtouch
  • iPhone: VoiceOver

PC端:

  • Windows: NVDA, JAWS
  • Chrome: ChromeVox
  • OSX: VoiceOver

条件有限,本人只研究了下ChromeVox和VoiceOver的使用,VoiceOver最简单的开启是是使用siri,语音“VoiceOver”或手动打开 “设置-通用-辅助功能-VoiceOver”。
当然在开启之前最好学几个简单的VoiceOver手势
VoiceOver部分手势

现状

我们知道在美国他们有无障碍标准的相关法律,比如美国残疾人法案(The Americans with Disabilities Act ),1973年的康复法案 Rehabilitation Act (504 节和第508节 )国际法 international laws。
我国信息无障碍构建起步较晚,从2003年“大连通信残疾人信息无障碍论坛”开始才进入公众视野。
来源于2016年4月《中国互联网视障用户基本情况报告》数据显示,目前国内预估有至少600万视障人士在使用智能手机,借助智能手机的读屏功能,他们可以跟普通用户一样看新闻、社交、打车、购物。不过调查也显示,66%的视障者认为目前国内信息无障碍水平一般,另有20%的视障者认为信息无障碍水平不好。
那怎样能让读屏软件能更好的读出我们的代码?
ARIA就是可以让屏幕阅读器准确识别网页中的内容,变化,状态的技术规范,可以让盲人用户也能无障碍阅读!
ARIA是Accessible Rich Internet Applications的缩写。它是W3C的Web无障碍推进组织(Web Accessibility Initiative / WAI)在2014年3月20日发布的可访问富互联网应用实现指南。

HTML中的一些ARIA

ARIA在HTML中由两部分组成,一个role,表示元素的作用;一个是aria- 表示元素的属性或状态

一些role值

role值 含义 role值 含义
alert 警告 alertdialog 警告框
application 应用 button 按钮
checkbox 复选框 grid 网格
gridcell 网格单元 group 组合
heading 应用程序标题 listbox 列表框
log 日志 menu 菜单
menubar 菜单栏 menuitem 菜单项
menuitemcheckbox 可复选的菜单项 menuitemradio 只能单选的菜单项
option 选项 presentation 陈述
progressbar 进度条 radio 单选
radiogroup 单选按钮组 region 区域
row separator 分割
slider 滑动条 spinbutton 微调
tab tab标签 tablist 标签列表
tabpanel 标签面板 timer 计数
toolbar 工具栏 tooltip 提示文本
tree 树形 treeitem 树结构选项

ARIA- 属性或状态
aria-label

  • 视觉上能理解,读屏软件不能理解的可以用aria-label,和img alt属性效果相同。
  • placeholder不能被读屏软件识别,如果不带label的form表单,需要用aria-label标注。个人测试iOS10上VoiceOver是可以读placeholder,如果两个属性同时存在会先读aria-label后读placeholder,chrome插件的Accessibility Developer Tools不识别placeholder。

aria-hidden
使用aria-hidden=”true”从可访问性树上隐藏元素,但对于用户而言,在屏幕上仍然可见,只是屏幕阅读器无法阅读。
隐藏元素的一些对比

方法 行为 读屏软件行为 兼容性
CSS: visibility:hidden; 从视觉中隐藏元素,但其原始空间仍然被占用(很像opacity:0) 不可读 兼容性好
CSS: display:none; 从视觉中隐藏元素,它的原始空间丢失,下一个元素将取代它的位置 不可读 兼容性好
HTML5: hidden属性 类似display:none 不可读 IE11+
aria-hidden = “true” 内容显示在浏览器,但读屏软件不可读 不可读 IE11+
CSS: .visuallyHidden类 视觉不显示,工作流不存在 可读 兼容性好

.visually-hidden {
position: absolute !important;
clip: rect(1px 1px 1px 1px); /* IE6, IE7 */
clip: rect(1px, 1px, 1px, 1px);
padding:0 !important;
border:0 !important;
height: 1px !important;
width: 1px !important;
overflow: hidden;
}
body:hover .visually-hidden a,
body:hover .visually-hidden input,
body:hover .visually-hidden button { display: none !important; }

aria-pressed
表示按下的状态,可选值有:true, false, mixed, undfined.默认为undfined, 表示按下状态未知;true表示元素往下(按钮按下);false表示元素抬起;mixed表示元素同时有按下和没有按下的状态

<div role="button" tabindex="0" aria-pressed="false" aria-disabled="false"></div>

aria-selected
表示选择状态,可选值有:true, false, undefined。 默认为undefined,表示元素选择状态未知;true表示元素已选择;false表示未被选中。

<div class="tabpanel">
  <ul class="tablist" role="tablist">
    <li role="tab" aria-selected="true" tabindex="0">春天</li>
    <li role="tab" aria-selected="false" tabindex="-1">夏天</li>
    <li role="tab" aria-selected="false" tabindex="-1">秋天</li>
    <li role="tab" aria-selected="false" tabindex="-1">冬天</li>
  </ul>
</div>

一些 ARIA的规则

1、元素使用ARIA roles必须是合法的ARIA role
建议这样做:

<div role="button"></div>
<div><div>

不要这样做:

<div role="datepicker"></div> <!-- Bad: "datepicker" is not an ARIA role -->
<div role="range"></div>      <!-- Bad: "range" is an _abstract_ ARIA role -->
<div role=""></div>           <!-- Bad: An empty ARIA role is not allowed -->

2、aria-labelledby属性应该引用存在于DOM中的元素
建议这样做:

<div id="label-element">
  Label for text input
</div>
<input type="text" aria-labelledby="label-element"></input>

不要这样做:

<div id="my-label">Label for text input</div>
<input type="text" aria-labelledby="the-label"></input>

3、具有ARIA role的元素必须具有该角色的所有必需属性
建议这样做:

<span role="checkbox" aria-checked="false" aria-labelledby="foo" tabindex="0"></span>

不要这样做:

<span role="checkbox" aria-labelledby="foo" tabindex="0"></span>

4、ARIA状态和属性值必须有效
建议这样做:

<span aria-hidden="true">foo</span>

不要这样做:

<span aria-hidden="yes">foo</span>

5、元素的ID不能随时出现在多个aria-owns属性中
不要这样做:

<input id="combo1" type="text" role="combobox" aria-labelledby="foo" aria-owns="list1"/>
 
<input id="combo2" type="text" role="combobox" aria-labelledby="foo" aria-owns="list1"/>
 
<ul id="list1" aria-expanded="true" role="listbox">
    <li role="option" tabindex="-1">Rainbow Trout</li>
    <li role="option" tabindex="-1">Brook Trout</li>
    <li role="option" tabindex="-1">Lake Trout</li>
</ul>

6、具有ARIA role的元素必须确保所需的所有元素存在
建议这样做:

<ul role="radiogroup" aria-labelledby="foo"> 
    <li id="radio1" tabindex="-1" role="radio" aria-checked="false">Rainbow Trout</li> 
    <li id="radio2" tabindex="-1" role="radio" aria-checked="false">Brook Trout</li>
    <li id="radio3" tabindex="0" role="radio" aria-checked="true">Lake Trout</li>
</ul>

不要这样做:

<ul role="radiogroup" aria-labelledby="foo"> 
    <li id="radio1" tabindex="-1">Rainbow Trout</li> 
    <li id="radio2" tabindex="-1">Brook Trout</li>
    <li id="radio3" tabindex="0">Lake Trout</li>
</ul>

7、具有ARIA role的元素必须在正确的范围内
建议这样做:

<div role="list"> 
    <span role="listitem">Rainbow Trout</span> 
    <span role="listitem">Brook Trout</span>
    <span role="listitem">Lake Trout</span>
</div>

不要这样做:

<div> 
    <span role="listitem">Rainbow Trout</span> 
    <span role="listitem">Brook Trout</span>
    <span role="listitem">Lake Trout</span>
</div>

8、元素需具有受支持的ARIA属性
建议这样做:

<ul role="radiogroup" aria-required="true" aria-labelledby="foo">
    <li tabindex="-1" role="radio" aria-checked="false">Rainbow Trout</li>
    <li tabindex="-1" role="radio" aria-checked="false">Brook Trout</li>
    <li tabindex="0" role="radio" aria-checked="true">Lake Trout</li>
</ul>

不要这样做:

<!-- Bad: the radio role does not support the aria-required property -->
<ul role="radiogroup" aria-labelledby="foo"> 
    <li aria-required="true" tabindex="-1" role="radio" aria-checked="false">Rainbow Trout</li> 
    <li aria-required="true" tabindex="-1" role="radio" aria-checked="false">Brook Trout</li>
    <li aria-required="true" tabindex="0" role="radio" aria-checked="true">Lake Trout</li>
</ul>

9、元素须具有有效的ARIA属性
建议这样做:

<div id="address_label">Enter your address</div>
<input aria-labelledby="address_label">

不要这样做:

<!-- Bad: Labeled using incorrectly spelled aria-labeledby -->
<div id="address_label">Enter your address</div>
<input aria-labeledby="address_label">

10、这些元素不支持ARIA role,状态和属性
eg: meta,html,script,style
建议这样做:

<meta charset="UTF-8">

不要这样做:

<meta charset="UTF-8" aria-hidden="false">

11、tab面板切换tab最好通过 aria-controls 或 aria-labelledby

HTML

1、网页应该具有标记中指示的内容的人文语言
建议这样做:

<!DOCTYPE html>
<html lang="fr">

不要这样做:

<!DOCTYPE html>
<html>

2、Dom中元素ID必须唯一
3、控件和媒体元素应该有标签
建议这样做:

<div id="address_label">Enter your address</div>
<input aria-labelledby="address_label">
 
<input aria-label="Enter your address">
 
<label for="address">Enter your address</label>
<input id="address">
 
<label>
  Address:
  <input>
</label>
 
<input title="Enter your address">
 
<video controls id="video">
  <source src="video.webm" type="video/webm"/>
</video>
<label for="video">Video of ducklings</label>

不要这样做:

<div>
  Enter your address:
  <input id="address">                    <!-- Bad: label not associated with control -->
</div>
 
<button class="enter_site"></button>      <!-- Bad: button has no text description -->
 
<input placeholder="Enter your address">  <!-- Bad: placeholder is used in place of a label -->

读屏软件会读以下使用form元素的名称

- aria-labelledby attribute
- aria-label attribute
- HTML <label>
- alt attribute, for <img> or <input type='img'> elements
- title attribute as a last resort.

4、图像应该具有alt属性,除非它们具有role= “presentation”

建议这样做:

<img src="flowers.jpg" alt="A vase containing a dozen red roses">
 
<img src="line.png" alt="">
 
<img src="dot.png" role="presentation">

不要这样做:

<img src="stateDiagram.jpg">

5、带有onclick元素必须是可对焦的

建议这样做:

<span onclick="doSomething();" tabindex="0">Click me!</span>
 
<span onclick="doSomething();" tabindex="-1">Click me too!</span>
 
<button id="button">Click me as well!</button>
<script>
  document.getElementById("button").addEventListener("click", doSomething);
</script>
 
<a href="javascript:void(0);" onclick="doSomething();">Click ALL the things!</a>

不要这样做:

<!-- Bad: span with onclick attribute has no tabindex -->
<span onclick="submitForm();">Submit</span>
 
<!-- Bad: anchor element without href is not focusable -->
<a onclick="showNextPage();">Next page</a>

可以聚焦的元素

- <input>, <button>, <select> and <textarea> elements which are not disabled
- <a> or <area> elements with an href attribute

6、视频元素应使用元素来提供字幕
建议这样做:

<video controls>
    <source src="video.webm" type="video/webm" />
    <track kind="captions" src="captions.vtt" type="text/vtt" srclang="en" label="English Captions" default />
</video>

7、表格必须有适当的标题
建议这样做:

<table> 
  <tr>
    <th>Header</th>
    <th>Header</th>
    <th>Header</th>
  </tr>
  <tr>
    <td>Cell</td>
    <td>Cell</td>
    <td>Cell</td>
  </tr>
</table>

不要这样做:

<table> 
  <tr>
    <td>Cell</td>
    <td>Cell</td>
    <td>Cell</td>
  </tr>
  <tr>
    <td>Cell</td>
    <td>Cell</td>
    <td>Cell</td>
  </tr>
</table>

8、描述性表格不要有标题
建议这样做:

<table role="presentation"> 
  <tr>
    <td>Cell</td>
    <td>Cell</td>
    <td>Cell</td>
  </tr>
  <tr>
    <td>Cell</td>
    <td>Cell</td>
    <td>Cell</td>
  </tr>
</table>

不要这样做:

<table role="presentation"> 
  <tr>
    <th>Header</th>
    <th>Header</th>
    <th>Header</th>
  </tr>
  <tr>
    <td>Cell</td>
    <td>Cell</td>
    <td>Cell</td>
  </tr>
</table>

 

CSS

WCAG 2.0 AAA 级要求正常文本最小对比度为 7:1,大号文本为 4.5:1,AA 级的建议是正常文本最小对比度为 4.5:1,大号字体为 3:1。
不要这样做:

<p style="color: gray">  <!-- Contrast ratio 3.95:1 -->
Warning: this product should not be used by any minor without adult supervision.
 
<h1 style="color: #BBB">Very subtle heading</h1>  <!-- Contrast ratio 1.92:1 -->

实践

领券中心是一个比较重要的优惠券领取入口,正好四期项目在修改,新增了一些aria的应用。

修改前检测截图:

image

主要问题:

1、img没有alt属性
修改后代码:

<img data-lazy-img="done" width="100" height="100" alt="" src="//img10.360buyimg.com/N7/s100x100_jfs/t7471/271/57601213/250196/b7b1a11c/598e7c2dN2377a3d3.jpg">

2、 form缺少label
修改后代码:

<input type="text" onkeydown="javascript:if(event.keyCode==13) search('quan-key');" autocomplete="off" id="quan-key" accesskey="s" class="text" aria-label="搜索框">

3、html标签缺少 lang属性
修改后代码:

<html lang="en">

后续:在iOS系统发现用VoiceOver测试 lang=”en” 无法读取中文,最终上线还是去掉这个属性设置。
4、 文字的对比度不合规范
image
这个建议和对应的设计师沟通,更改色值
5、直接使用h3标签,没有h1,h2标签
修改后代码:

<div id="logo-2014">
<h1><a href="http://www.jd.com/" class="logo">京东</a></h1>
<div class="extra">
    <h2 id="channel">领券中心</h2>
</div>
</div>

6、 无效链接地址
修改前代码:

<a href="#none" class="btn btn-def" index="0"  ><span class="txt">立即领取</span></a>

修改后代码

<a href="javascript:;" class="btn btn-def" role="button" tabindex="0"><span class="txt">立即领取</span></a>

7、全部分类隐藏部分tab键不可访问
配合js改动,修改后代码:

.cate-more:focus + .list-more {
  visibility: visible;
  opacity: 1;
  top: 39px;
}
<a href="javascript:;" class="cate-more"   data-target="listmore">全部分类<span class="ci-right"><s>◇</s></span></a>

8、 tab切换时券面信息读取不到
修改后代码:

<div class="q-type" tabindex="0"><div class="q-price"><em>¥</em><strong>30</strong><span class="q-limit">满100可用</span></div><div class="q-range"><span class="superimposed">可叠加</span>纤诗月旗舰店</div><div class="q-progress"><span class="txt">已抢85%</span><span class="progress-wrap"><span class="progress-bg"></span><span class="progress" style="width:85%"></span></span></div></div>

修改后检测截图:

image
以上内容都是是使用WAVE web accessibility evaluation tool检测修改(检测标准 WCAG 2.0 Checklist,Section 508 Checklist)

总结

网站上的大多数可访问性错误可能都是由于缺乏意识而不是恶意或冷漠,希望通过本文大家能对无障碍有更多的认知,并在平时开发的中应用起来,保证页面上的元素通过Tab键能直接访问,页面上所有的元素能被读屏软件正确的读取。

chrome Tools
Accessibility DeveLoper Tools
ChromeVox
WAVE

参考文献
www.w3.org/TR/wai-aria…
dev.opera.com/articles/ux…
rawgit.com/w3c/aria/ma…
aerolab.co/blog/web-ac…
www.tisi.org/Article/lis…
webaim.org/