欢迎订阅专栏:万字解析 WCAG 2.1 AA 网页内容无障碍指南
前言
万字解析 WCAG 2.1 AA 网页内容无障碍指南(二):1. Perceivable 可感知性 - 掘金 上篇文章介绍了 WCAG 2.1 指南中第一章节的每条规则。本篇继续介绍第二章节 2. Operable 可操作性 中的规则。
WCAG 2.1 官方文档:Web Content Accessibility Guidelines (WCAG) 2.1 中文:Web内容无障碍指南 (WCAG) 2.1
2.Operable 可操作性
2.1 Keyboard Accessible 键盘可访问
2.1.1 Keyboard 键盘 A
内容的所有功能都可以通过键盘接口实现操作并且没有对每次键击做特定时限要求。除非底层功能是依赖用户的移动路径并且不做为端点的输入方法。
解读:要求通过键盘可以操作所有页面功能,包括所有可交互元素、弹框内部等,都需要支持键盘焦点及操作,并且行为正确。详见下一个规则 2.1.2。
2.1.2 No Keyboard Trap 无键盘陷阱 A
如果可使用键盘接口能将焦点到移动到某网页组件上,那么可以只使用键盘接口操作也可以将焦点从当前组件移开,如果需要未修改的方向键或Tab键或其它标准退出方式以外的操作,要告知用户离开焦点的方法。
解读:无键盘陷阱,能通过tab键入的功能区域,也必须能且仅能通过tab键出。 2.1 规则主要是介绍键盘的规范,要求:
- 通过Tab键(包括Shift+Tab)操作页面时,焦点需要从左到右,从上到下这种正确顺序来。
- 如果有点按钮,Dialog等Modal弹出时,焦点应该自动进入弹框内元素,而且此时Tab操作需要在弹框内循环,关闭弹框(需要支持键盘Esc)需要将焦点自动定位到之前操作的按钮上。Modal弹框包括Dialog、select dropdown的下拉框、侧滑弹框等等。
- 如果有loading等遮罩情况,焦点需要定位在遮罩上,并且不允许tab到遮罩底部元素,直到遮罩消失。
开发方案:
- html dom顺序跟Tab顺序是一致的,可能需要定义
tabindex
来给非交互元素加焦点,或者定义顺序。 - 对于弹框Modal,或者loading等遮罩,需要给弹框(遮罩)容器注册keydown事件,然后通过判断当前获得焦点的元素(
document.activeElement
)、按键、然后判断当前容器内的focusable元素列表(jQuery :tabbable
),来做到Modal内部焦点循环,避免焦点定位到底部遮罩下面元素上。参考下面代码。
这里参考了 rc-dialog 库,antd design就是基于这个库封装的Modal组件,实现了下简易的modal内循环tab焦点:
jsx
<div id="modal" class="modal">
<div id="modal-start-dom" tabindex="0" class="modal-hidden" aria-hidden="true"></div>
<button>button 1</button>
<button>button 2</button>
<button>button 3</button>
<div id="modal-end-dom" tabindex="0" class="modal-hidden" aria-hidden="true"></div>
</div>
const startDom = document.getElementById('modal-start-dom');
const endDom = document.getElementById('modal-end-dom');
document.getElementById('modal').addEventListener('keydown', e => {
if (e.keyCode === 27) { // 按ESC关闭modal
e.stopPropagation();
// close modal
// focus trigger button
return;
}
else if (e.keyCode === 9) { // 按Tab
const activeElement = document.activeElement;
if (!e.shiftKey && activeElement === endDom) {
// 按Tab,并且当前焦点在最后一个dom时,需要将第一个dom获得焦点
startDom.focus();
}
else if (e.shiftKey && activeElement === startDom) {
// 按Shift+Tab,并且当前焦点在第一个dom时,需要将最后一个dom获得焦点
endDom.focus();
}
}
})
rc-dialog源码位置:
2.1.4 Character Key Shortcuts 字符键快捷键 A
如果仅使用字母(包括大写和小写字母),标点符号,数字或符号字符在内容中实现键盘快捷键,则至少满足下列条件之一:
关掉 有一种机制可以关闭快捷方式;
重映射 可以使用一种机制重新映射快捷方式以使用一个或多个不可打印的键盘字符(例如Ctrl,Alt等);
仅在焦点上有效 用户界面组件的键盘快捷键仅在该组件具有焦点时才处于活动状态。
解读:关于快捷键的交互规范,只适用于支持快捷键功能的网页;否则不适用。
2.2 Enough Time 充足的时间
2.2.1 Timing Adjustable 定时可调 A
对于由内容设置的每一个时间限制,以下部分至少有个一为真:
关闭 允许用户达到时间期限之前,关闭时间限制;或
调整 允许用户达到时间期限前,调整时间限制。调整范围要放宽,至少是默认设置长度的10倍;或
延长 在超时之前向用户发出警告,并给用户至少20秒的时间,使用户可以通过简单的动作来延长时间(例如"按空格键"),并允许用户延长期限至少10次;或
实时特例 时间限制是一个实时事件(例如拍卖)的必要部分,可能没有时间限制的替代方法;或
必需特例 时间限制是必需的,延长时限将导致行为失效;或
20小时特例 时间限制超过20个小时。
解读:举一个session timeout的例子,用户登陆系统后,session有一定时长timeout时间,如果不操作页面到了时间就会弹框提醒重现登陆。
按这个规范,就需要在剩余一定时长时,弹框提示还剩多长时间timeout,并且可以让用户点击弹框按钮延长(重置)timeout。
2.2.2 Pause, Stop, Hide 暂停、停止、隐藏 A
对于运动、闪烁、滚动或自动更新的信息,以下部分全部为真:
运动、闪烁、滚动 任何运动、闪烁或滚动的信息(1)自动启动,(2)持续时间超过5秒钟,(3)与其他内容同时呈现。对于这些信息,提供一个机制可使用户暂停,停止或隐藏,除非运动、闪烁、或滚动是某个行为的必需部分;以及
自动更新 任何自动更新的信息(1)自动启动(2)与其他内容同时呈现。对于这些信息,提供一个机制可使用户暂停、停止、或隐藏,或控制更新的频率,除非自动更新是某个行为的必需部分。
解读:字面意思,得有个机制可以让用户主动暂停、停止等。
2.3 Seizures and Physical Reactions 癫痫和身体反应
2.3.1 Three Flashes or Below Threshold 闪光三次或低于阈值 A
网页不包含任何闪光超过3次/秒的内容,或闪光低于一般闪光和红色闪光阈值。
解读:
- 运动、闪烁、滚动:尽量不要有这种design交互。如果有需要按规范描述设计。
- 自动更新:规范类似上条,但是暂时没想出来有什么样的设计算是这种情况。
关于闪烁与癫痫 ,页面上闪烁的画面,对普通人没什么影响,顶多比较晃眼,但对光敏性癫痫患者来说,这些闪烁且自动播放的动图会让诱发他们的癫痫症状,严重甚至死亡。所以网页上尽量不要有闪烁的效果,这也是规定符合WCAG无障碍指南的重要性。
2.4 Navigable 可导航性
2.4.1 Bypass Blocks 绕过模块 A
解读:需要添加skip button,并且是页面第一个可获得焦点,并且点击可以跳过header、导航这些重复内容,焦点定位到内容元素上。
参考下面例子:
- 在body里添加
a
标签,必须是body
第一个子元素,需要设置href
,不然无法获取焦点。 a
标签默认不可见,这里用的是设置高宽都是1px。- 焦点从浏览器地址栏Tab到页面时,
a
标签第一个获得焦点,可见,位置是右上角绝对定位,并且设置大点zindex
。 - 添加点击事件,先
preventDefault
阻止默认跳转行为,再让content区域第一个可获得焦点元素input
获得焦点。- 也可以用
jQuery UI
的 :focusable :tabbable 方法获取content dom下第一个可获得焦点元素。
- 也可以用
a
标签失去焦点,不可见。
jsx
<head>
<style>
#skip-btn {
position: absolute !important;
}
#skip-btn:focus {
background-color: blue;
color: white;
z-index: 999;
}
#skip-btn:not(:focus) {
width: 1px !important;
height: 1px !important;
padding: 0 !important;
overflow: hidden !important;
clip: rect(1px, 1px, 1px, 1px) !important;
border: 0 !important;
}
</style>
</head>
<body>
<a href="#" id="skip-btn">Skip to content</a>
<div id="nav">
<a href="/page1">page1</a>
<a href="/page2">page2</a>
<a href="/about">about</a>
</div>
<div id="content">
<div>title</div>
<input id="input1" type="text">
</div>
<script>
document.getElementById('skip-btn').onclick = function (e) {
e.preventDefault();
document.getElementById('input1').focus();
}
</script>
</body>
这是GitHub的效果,但是它点回车后,焦点是在一个template div上,再tab焦点才会到输入框里,不知道为啥。
2.4.2 Page Titled 网页标题 A
网页提供标题,以描述主题或用途。
解读:页面需要标题,且每个页面都需要有title(head里的title标签),且有意义
- 首先网站得有一个站点名称的title,比如掘金就是:稀土掘金,用来设置主页title。
- 然后对于每个网页,都需要设置对应网页的title,不能每个页都用同样的title,比如掘金的沸点页,如下图。
实现很简单,每个页面动态设置就行:document.title = 'your title'
2.4.3 Focus Order 聚焦顺序 A
解读:键盘操作焦点必须有顺序,并且顺序有意义、正确。
举个常见例子,比如页面顶部有导航,然后有的导航有二级菜单,那么就需要做成允许通过键盘展开二级菜单,并且菜单内的二级导航也可以被键盘操作,且支持Esc关闭二级菜单继续操作别的导航。这里的二级菜单也算是Popup的一种,详见 2.1。
2.4.4 Link Purpose (In Context) 链接目的(在上下文里) A
每个链接目的的确定可通过:单独的链接文本,或者是链接文本联系其编程式确定的连接上下文除非链接的目的也会困惑普通用户。
解读:这个到现在也没理解啥意思。
2.4.5 Multiple Ways 多种方法 AA
解读:有一种以上方式可以定位到想访问的页面。比如导航、面包屑、sitemap等。
2.4.6 Headings and Labels 标题和标签 AA
标题和标签说明主题或目的。
解读:页面标题(一般是加粗大字体)和label(表单field name)是有目的性的,即有意义的。
2.4.7 Focus Visible 焦点可见 AA
任何键盘可操作的用户界面应有一套操作模式,在该模式里键盘焦点指示是可见的。
解读:页面元素获得焦点时,需要有明显的样式。比如黑框、虚线框等。
这里用到一个css伪类:focus-visible
,html自带一个全局的focus样式,如下图,是一个黑色的边框。
当然你也可以全局覆盖下,如下:
css
:focus-visible {
outline: 1px dashed red; // 红色虚线框
}
效果:
2.5 Input Modalities 输入方式
2.5.1 Pointer Gestures 指针手势 A
除非基于多点或基于路径的手势是必要的,否则使用多点或基于路径的手势进行操作的所有功能都可以使用单一指针操作而无需基于路径的手势。
解读:如果有鼠标拖拽操作的功能,得保证仅通过鼠标点击、和仅通过键盘操作,两种操作情况下实现页面所有功能交互。并且拖拽过程中也有规范说明,描述很概念,很模糊。
这里就重点说下仅通过鼠标点击、和仅通过键盘操作:
- 为什么:我理解是为了考虑一些残障特殊人群,比如手部受伤或者残疾人员,用不了鼠标,只能用键盘点击,如果某些功能只能通过鼠标拖拽完成,那么他们就没办法操作了。
- 怎么实现:比如一个表单项拖拽功能,可以在可以拖拽的区域,添加一个dropdown,允许通过键盘Tab进来,然后选择想放置的表单项;再或者一个方块拖拽位置功能,可以让方块获得焦点时,通过方向键调整位置,每次点击指定方向移动5px之类的。
2.5.2 Pointer Cancellation 指针取消 A
没有向下事件 指针的向下事件不用于执行函数的任何部分;
终止或撤销 该函数的完成是在向上事件上完成的,并且有一种机制可以在完成之前中止该函数或在完成之后撤消该函数;
逆转 上行事件逆转了前一次事件的任何结果;
必要 完成下行事件的必要功能。
解读:同上。
2.5.3 Label in Name 名称中的标签 A
解读:表单中的交互控件,需要设置label标记(aria-label
或aria-labelledby
属性),让用户通过辅助软件能知道这个field的作用。
主要是针对读屏辅助软件,让盲人用读屏软件键盘操作表单时,能知道对应表单项的功能描述,举个例子:
html
<div>
<input type="checkbox" id="checkbox1" name="name1" value="">
<label for="checkbox1">checkbox label</label>
</div>
<div>
<label for="input1">Name1</label>
<input type="text" id="input1">
</div>
<div>
<label id="label2">Name2</label>
<input type="text" aria-labelledby="label2">
</div>
需要用for
或者aria-labelledby
属性关联label
与input
元素。读屏效果如下:
可以看到当checkbox、input获得焦点时,对应的label也会被读出来。
至于用for
还是aria-labelledby
,区别就是用for
可以再点击label时,checkbox会check、input会自动获得焦点,用aria-labelledby
则不会这样。
另外也可以直接用aria-label
设置固定label文字:<input type="text" aria-label="Name2">
2.5.4 Motion Actuation 运动驱动 A
可以通过设备运动或用户运动操作的功能也可以由用户界面组件操作,并且可以禁用对运动的响应以防止意外启动,除非在以下情况下:
支持的接口 该动作用于通过可访问性支持的界面操作功能;
必要 该动作对于该功能至关重要,这样做会使活动无效。
解读:只适用于设备运动的功能交互,比如手机的重力感应、摇一摇那种。
比如有些摇一摇的功能,需要假设用户手机不能、或者特殊人员无法做到摇一摇情况下,也可以通过别的方式操作,比如加个可点击按钮等。
总结
WCAG 2.1 指南中的第二章节 2. Operable 可操作性 就说完了,下一篇会继续介绍第三章节 3. Understandable 可理解性 中的规则。
欢迎订阅专栏:juejin.cn/column/7300...