首先要了解下什么是 "无障碍访问"。关于 "无障碍访问" 大家或多或少都有所耳闻,于是可能会产生这么一个误解,认为所谓 "无障碍访问" 就是让势力有缺陷的人可以自如使用访问自己的产品。实际上,"无障碍访问" 涵盖的范围要广泛的多。
所谓 "无障碍访问",指的是各类设备均可以无障碍访问。例如鼠标、键盘、读屏软件或设备等。
所以,大家不妨思考这么几个问题?
以上这种场景的适配和支持就属于 "无障碍访问" 技术范畴。于是,请继续思考:
focus 时候会外发光?
- <input>
这其实涉及到 "键盘可访问性",当用户鼠标出现异常,比方说我的 iMac 鼠标多次忘记充电,只能键盘操作,此时往往都是使用 Tab 键按顺序(如果没有设置
)让控件元素获取焦点,并默认
- tabindex
高亮。
- outline
所以,对于 to C 也就是面向大众用户的网站,使用如下的 CSS 是非常业余的:
- * {
- outline: 0 none;
- }
但是,有小伙伴会吐槽了:"我也不想去掉啊,但是设计师看不惯默认的发光效果啊!"
如果是对效果不满意,那好办,改成设计师喜欢的
效果就好了,例如:
- focus
- input {
- outline: 0,
- none;
- }
- input: focus {
- border - color: HighLight;
- }
- <a href role="button">
这里出现的
属性就是 ARIA html 无障碍访问中最常用的属性之一。
- role
ARIA 全称 "Accessible Rich Internet Applications(可访问的富互联网应用)",在当下讨论 ARIA 基本上都是为读屏设备或软件,而读屏设备或软件的使用者多是视力障碍人员,因此,ARIA 约定俗成就成了对视障用户的无障碍访问支持技术的代称了。
首先,了解下常见的读屏软件,如下列表:
移动端:
桌面端:
由于本人精力有限,因此,目前仅试验了移动端 - iPhone-VoiceOver,iOS 10.*.* 版本下的 ARIA 支持情况。对于其他软件,我猜想应该都是类似的,毕竟有规范在那里。
ARIA 规范还在不断更新中,且相当积极,因此,规范往往会比软件的支持超前,因此,本文下面的内容不涉及新的属性特性。
ARIA 总共有 3 大部分组成,如下:
我之前的文章 " " 有非常详尽的翻译展示,我这里列举一些常用的:
- role="tab",
- role="button",
- role="radio",
- role="checkbox",
- role="link",
- …
- aria-haspopup,
- aria-label,
- aria-owns
- …
- aria - checked,
- aria - checked,
- ,
- aria - selected,
- aria - expanded,
- ,
- ,
- aria - hidden,
- ,
- aria - invalid,
- …
好,热身完毕,下面开始本文的重点了,基于 VoiceOver 的移动 web 站无障碍访问实战。
最快捷的是使用 siri,"打开 VoiceOver",如果 siri 卖萌说不知道 VoiceOver 是什么鬼?那就手动开始,路径为:设置→通用→辅助功能→VoiceOver(在第一个,见下图)
在 iPhone 上 VoiceOver 开始后,操作方式有个根本的变化:
或
- touchstart
行为完全变成了内容识别与读取,没有任何 web 交互行为的发生!
- touchmove
或
- touchstart
选中,然后再双击,直接双击是没有用的;
- touchmove
或
- touchstart
选中,然后连续轻触并滑动(手指不要抬起);
- touchmove
所以,很多人不小心打开 VoiceOver 后发现关不了了,就是因为还是老的操作习惯。想要关闭,最快捷 siri;如果只卖萌不行动,那就手动按部就班关闭:(轻触,再双击)设置→(轻触,再双击)通用→(轻触,再双击)辅助功能→(轻触,再双击)VoiceOver->(轻触,再双击)关闭。
首先,大家应该都知道,HTML 元素还可以分为非替换元素和替换元素,常见的替换元素包括
,
- <img>
,
- <input>
,
- <img>
,
- <button>
,
- <iframe>
,
- <video>
等。
- <object>
除了内容可替换以及一些 CSS 行为差异外,在 ARIA 无障碍访问这一领域也是有着巨大的差异的。
非 VoiceOver 状态,你点击页面空白,是不会有什么反应的,但是开启 VoiceOver 后则完全不同,touch 页面任何区域一定会有信息读取,包括空白区域,而轻触空白区域的读取遵循下面 2 个规则:
所谓 "就近原则",比方说点击下图所示的位置:
结果选中和读取的内容是 "免费 链接 导航 标志性内容",哪个近读哪个。
所谓 "穿透规则",比方说点击下图所示的半透明黑色蒙层位置,请问读出来的信息是?
结果不是 "信仰神国",也不是 "读至…",读取的信息而是蒙层下面的图片列表信息!没错,直接穿透了。这个和正常浏览方式下的点击世界观是不一样的。但是,显然,此处穿透不是我们想要的,怎么避免呢?这个后面会介绍。
- <a href>
- <i class="icon-free">
- </i>
- <!-- VoiceOver忽略 -->
- <h4>
- 免费
- </h4>
- </a>
属性描述内容,那会不会读取呢?
- title
- <i class="icon-free" title="图标">
- </i>
- <!-- VoiceOver读不读呢? -->
隐藏的,那会不会读取呢?
- display:none
- <i class="icon-free">
- <span hidden>
- 图标
- </span>
- </i>
隐藏,那会不会读取呢?
- visibility:hidden
- <i class="icon-free">
- <span style="visibility:hidden;">
- 图标
- </span>
- </i>
缩进让文字隐藏到屏幕之外,那总该要读取了吧?
- text-indent
- <i class="icon" style="text-indent:-999px;">
- 图标
- </i>
缩进不要那么猛,仅仅是图标容器外,但是在屏幕内,那会不会读取呢?
- text-indent
答案是: 会读取! 但是,选中时候的外框明显不在图标所在的位置,而是文字所在的位置。
- <i class="icon" style="text-indent:-10%;">
- 图标
- </i>
属性隐藏,那会不会读取呢?
- clip
- <i class="icon">
- <span style="position:absolute;clip:rect(0,0,0,0);">
- 图标
- </span>
- </i>
- <i class="icon">
- <span style="position:absolute;left:-999px;">
- 图标
- </span>
- </i>
- <i class="icon">
- <span style="position:relative;left:-999px;">
- 图标
- </span>
- </i>
- <i class="icon" style="color:transparent;">
- 图标
- </i>
如下一段简单常见的 HTML 代码:
- <p>
- 总共消费
- <output>
- 500
- </output>
- 元
- </p>
请问,
或
- touchstart
该
- touchmove
元素的时候读取的信息是?
- <p>
但是,如果是
~
- <h1>
标题元素,则例外,例如:
- <h6>
- <h6>
- 总共消费
- <output>
- 500
- </output>
- 元
- </h6>
读 "总共消费 500 元 标题级别 6"。
比方说一开始的这个图:
还是上面那个免费图形:
则
- <i class="icon" role="img" title="图标">
- </i>
属性值是会读取的,这里会读 "图标 图像"。
- title
描述信息也会读取:
- aria-label
- <i class="icon" role="img" aria-label="图标">
- </i>
非
- role="button"
隐藏文本均读取,也就是
- display
透明隐藏,
- color
隐藏,
- font-size:0
隐藏,
- visibility:hidden
缩进隐藏,
- text-indent
屏幕外隐藏。。。都是读取的。 但是,如果是
- absolute
,则忽略:
- display:none
- <i class="icon" role="button">
- <span hidden>
- 图标
- </span>
- <!-- "图标"不读取 -->
- </i>
元素不可读不可点。
- aria-hidden="true"
- <i class="icon" role="button">
- <span aria-hidden="true">
- 图标
- </span>
- <!-- "图标"不读取 -->
- </i>
是 ARIA 无障碍处理售后非常常用的一个属性,所有的装饰性元素或者点击区域太小的元素都应该设置
- aria-hidden="true"
,避免无谓信息对用户的干扰。
- aria-hidden="true"
读:"图标,按钮",明显停顿后,"示意"。注意,读
- <i class="icon" role="button" title="示意">
- 图标
- </i>
属性之前有个非常非常明显的停顿,停顿时间之长,感觉就像朗读人断片了一样。
- title
作用和文字内容类似,但是优先级更高。也就是同时存在的时候,文字内容读取会被忽略,例如:
- aria-label
读:"图标 1,按钮",明显停顿后,"示意"。"图标 2" 不会读取,被忽略!
- <i role="button" aria-label="图标1" title="示意">
- 图标2
- </i>
或
- aria-labelledby
,例如:
- aria-describedby
读:"这是一个… 图标,按钮"。 可以看到,目前描述元素即使
- <i role="button" aria-labelledby="id">
- 图标2
- </i>
- <p hidden id="id">
- 这是一个...图标
- </p>
也是可以读取的。
- display:none
或
- aria-labelledby
的属性值对应描述信息元素的
- aria-describedby
属性值,可以是多个,使用逗号分隔,例如:
- id
- <i role="button" aria-labelledby="id1,id2,id3">
- 图标2
- </i>
会读 "导航,标志性内容",
- <nav>
读 "横幅,标志性内容",
- <header>
读 "页脚,标志性内容"。 然后,若触发的是子元素,仅第一次触发读取上面的信息。例如,导航中有两个链接,则轻触第一个的时候,读 "xx, 链接,导航,标志性内容",紧接着轻触第二个链接的时候,仅仅会读 ""xx, 链接"。 出乎意料的是,
- <footer>
,
- <ul>
不会读列表,需要增加
- <ol>
,这样,触发
- role="listbox"
元素的时候会读 "列表,第一个"。 所有
- <li;>
原生控件都能准确读取,包括状态。因此,一定要养成基于元素表单控件实现交互效果的习惯,控件丑没关系,透明度
- form
覆盖处理之,例如,单选项,复选框效果等。
- opacity:0
,
- role="button"
,
- role="tab"
,
- role="heading"
等。
- role="combobox"
读:"共消费 500 元,按钮"。 虽然很好地解决了多个内联元素读取 "断片" 的问题,但是,不合语义,明明不是按钮,你说是按钮,问题很大。 后来,经过我的各种尝试,发现了一个 无语义文本连读技巧 ,就是使用:
- <p role="button">
- 总共消费
- <output>
- 500
- </output>
- 元
- </p>
。
- role="option"
属性值设置会覆盖原始的语义,例如:
- role
读:"共消费 500 元",不会提示 "链接"。
- <a href role="option">
- 总共消费
- <output>
- 500
- </output>
- 元
- </a>
读:"共消费 500 元",不会提示 "链接"。 如果遇到这种尴尬,可以这么处理:
- <li role="option">
- <a href>
- 总共消费
- <output>
- 500
- </output>
- 元
- </a>
- </li>
也就是原语义外置。此时读:"共消费 500 元,链接",这下没毛病了。
- <li role="link">
- <a href role="option">
- 总共消费
- <output>
- 500
- </output>
- 元
- </a>
- </li>
是高成本操作,因此,需要增加详细的描述信息,否则用户根本不知道是个什么鬼?例如下面一段截图示意:
- touchmove
定义是菜单项,然后使用
- role="menuitem"
标记是展开还是收起,VoiceOver 会自动根据此状态值读出对应的中文状态描述的。
- aria-expanded
展开时候
- <a href class="icon-more" title="更多" role="menuitem" aria-expanded="false">
- </a>
设为
- aria-expanded
:
- true
上面例子中的
- <a href class="icon-more" title="收起更多" role="menuitem" aria-expanded="true">
- </a>
就是 ARIA 中的状态属性,常用的其他属性还包括(需要配合特定的
- aria-expanded
属性值才有效):
- role
展开还是收起,菜单,自定义下拉等用的比较多。
- aria-expanded
选中还是未选。
- aria-checked
选中还是未选。
- aria-selected
禁用还是可用。
- aria-disabled
隐藏还是显示。
- aria-hidden
验证正确还是错误。
- aria-invalid
和
- aria-checked
含义类似,那什么时候该用什么呢?
- aria-selected
多用在单选复选上,
- aria-checked
多用在下拉列表上,或者选项卡(
- aria-selected
)上。 说到
- role="tab"
选项卡,有一个注意点需要提一下,
- role="tab"
一定要加在平级的兄弟元素上,例如:
- role="tab"
千万不要这样:
- <nav>
- <h3 role="tab">
- <a href>
- 选项卡1
- </a>
- </h3>
- <h3 role="tab">
- <a href>
- 选项卡2
- </a>
- </h3>
- <h3 role="tab">
- <a href>
- 选项卡3
- </a>
- </h3>
- </nav>
因为后面每个选项卡会认为就一个单独的选项卡,读 "共 1 个",前者可以正确读 "共 3 个"。另外,VoiceOver 读取的时候不是读 "选项卡",听上去是 "标间",开房吗?
- <nav>
- <h3>
- <a href role="tab">
- 选项卡1
- </a>
- </h3>
- <h3>
- <a href role="tab">
- 选项卡2
- </a>
- </h3>
- <h3>
- <a href role="tab">
- 选项卡3
- </a>
- </h3>
- </nav>
,如果点击整片区域都有行为,不要取巧使用冒泡,因为在无障碍处理的时候会很啰嗦,直接使用一个透明图层覆盖,设置合适的
- role
以及描述;
- role
属性而不是
- aria-checked
类名,可以有效降低后期无障碍支持成本。
- .checked
交互,因为开启读屏模式后,
- touchmove
交互成本很高;
- touchmove
来源: