无障碍访问
- 1、键盘的无障碍访问
- 1.1、务必了解的tabindex属性
- 1.2、深入了解快捷键设置属性accesskey
- 1.2.1、隐藏的元素能否触发accesskey快捷访问
- 1.2.2、多个元素使用相同的accesskey属性值会怎样
- 1.2.3、约定俗成的开发策略
- 2、屏幕阅读无障碍访问
- 2.1、尽可能使用原生元素和属性
- 2.2、系统介绍ARIA规范属性
- 2.2.1、ARIA role属性
- 2.2.2、ARIA常规属性
- 2.2.3、ARIA状态属性
之所以要强调HTML的语义化,一方面是部分HTML自带交互行为,有助于降低Web开发成本;另一方面就是它无障碍访问的能力。
无障碍访问包括很多方面,如颜色的对比度、按钮的大小,以及辅助设备的访问与识别,如鼠标和键盘的访问能力、屏幕阅读器或翻译工具的访问能力、虚拟键盘使用的能力等。这些都需要你对HTML有所了解才能做得出色。
由于本书是介绍HTML的,因此仅会介绍与之相关的无障碍访问,包括物理键盘、屏幕阅读器及移动设备虚拟键盘。
1、键盘的无障碍访问
对于Web网页而言,键盘的访问能力是最基本的,因为不仅视力有障碍的人群需要网页有键盘访问能力,即使是正常用户,也需要网页中的控件元素可以通过键盘访问,如按回车键提交表单、下拉列表的上下键选取等。随着智能电视的普及,在电视机上也能访问网页,此时遥控器就扮演了键盘的角色,如果网页内容无法被键盘访问,那么该网页在电视机上就不可用。
其实,做好网页的键盘无障碍访问特性很简单,遵循以下原则即可:所有具有点击行为的控件元素一定要使用<a>元素或者<button>元素,如果能做到这一点,那么即使没有鼠标,网页也是基本可用的,因为这两个元素天然可以被Tab键索引,天然支持按回车键触发点击行为。
如果万不得已使用<div>元素或<span>元素模拟按钮,那么一定要设置tabindex=“0”,值必须是0,至于原因,且听我慢慢道来。
1.1、务必了解的tabindex属性
对于键盘访问,最重要的HTML属性莫过于tabindex属性了,这是一个全局属性,顾名思义,就是Tab+index,也就是可以使用Tab键索引元素。该属性可以让任意的元素变成键盘可访问元素,支持的值包括-1、0,以及1~32767的整数,如果属性值超过32767,则会按照-1渲染。
不同的tabindex属性值有着不同的含义。
当tabindex属性值为-1时,元素可以获得焦点(点击或执行JavaScript的focus()方法),但是无法被Tab键索引。该值常用来转移焦点,例如可以将弹窗上的元素设置为tabindex=“-1”,这样当弹窗出现时,焦点就会自动转移到弹窗上,而不是弹窗后的页面上。
不过原生的<dialog>元素并不需要这么处理,因为它本身就支持焦点转移,并且对于<dialog>元素而言,tabindex属性是无效的。tabindex="-1"的另外一个经典应用就是iOS Safari浏览器的无法失焦问题,例如如下代码所示的<a>元素在点击之后会被聚焦(focus),但是此时你再点击页面空白区域,该元素是不会失焦的。
解决方法就是找到层级足够高的祖先元素(不能是<body>元素,可以是<body>子元素),然后设置tabindex=-1,任意的非控件HTML元素在设置了tabindex=-1属性后,点击的时候都是会有聚焦状态的。也就是点击空白区域,会把原本附着在链接上的焦点转移到祖先元素上,于是blur失焦就正常触发了。
不过,根据测试这个持续了近10年的体验问题在iOS 14之后被修复了。
当tabindex属性值为0时,元素可以被Tab键索引,且索引顺序和<input>、<button>这类控件元素一致。
当tabindex属性值是自然数的时候,则
- 对于IE、Edge及N年前的Chrome等浏览器,tabindex是自然数的元素的索引顺序级别最高,然后才是tabindex=0的元素。
- 在现在主流浏览器下,tabindex=0的元素的索引顺序级别最高,tabindex是自然数的元素的索引顺序靠后,甚至比浏览器窗体自身的控件元素的聚焦顺序还要靠后。表现为
- tabindex="0"的元素最先被聚焦,随后浏览器自身各个按钮和地址栏被聚焦,最后才是tabindex="1"的元素被聚集。
- tabindex="1"的元素在通过Tab键索引后聚焦的是tabindex="0"的元素,tabindex="0"的元素在按下Shift+Tab键后聚焦的是tabindex="1"的元素。
- tabindex=2的元素一定比tabindex=1的元素的索引值靠后。
基本上,tabindex属性要么被设置为-1,要么被设置为0,设置为自然数的场景现在已经很少见了。
1.2、深入了解快捷键设置属性accesskey
可能很多读者都不知道,只要在HTML代码上设置一个属性,就可以让浏览器支持我们自定义的快捷访问方式,而这个神奇的属性就是本节要介绍的accesskey属性。
在HTML 4.0.1中,accesskey属性可以作用在以下元素上:<a>、<area>、<button>、<input>、<label>、<legend>及<textarea>。在HTML 5中,accesskey属性可以作用在任意元素上,变成了HTML全局属性。
例如,作用在<div>元素上:
这里的属性值“1”对应的就是键盘上的数字1,如果是accesskey=“a”,则“a”对应的就是键盘上的字母a。
需要注意的是,虽然accesskey属性值和键盘相对应,但并不是说直接按下这个键就能快捷访问。浏览器是通过快捷键组合访问的方式进行页面元素的快速访问的,具体的组合方式参见下表(注意,随着浏览器的迭代,快捷键的组合方式可能会有调整)。
IE浏览器和Chrome浏览器的组合键是一样的,但是其交互行为却和其他浏览器都不一样,例如:
在Windows操作系统下,按下Alt+1组合键,IE浏览器只是让<a>元素获得焦点,但是其他所有浏览器都会直接触发click行为。在我看来,IE浏览器的这种行为是不友好的,原因在于,类似<a>、<button>这样的元素还可以在获得焦点后按下回车键触发click行为,但是<div>这类元素是无法通过键盘触发点击行为的,这就使<div>等元素设置的accesskey属性没有任何价值。相比之下,Chrome等浏览器就没有这个问题,如以下HTML代码:
此时,我在Windows操作系统下,按下Alt+3组合键,结果“测试”二字变成了红色,如图所示。
这说明直接触发了绑定的click点击事件。
1.2.1、隐藏的元素能否触发accesskey快捷访问
对于一个元素,如果CSS display属性的计算值是none,则无法通过Tab键进行索引聚焦。那么,设置的accesskey快捷访问是否可以访问呢?
根据我的测试,Chrome浏览器和Firefox浏览器是可以的,元素即使隐藏,只要设置了accesskey快捷访问,按下对应组合键的时候就能触发click行为。例如:
在Windows操作系统下,按下Alt+1组合键,页面会直接刷新。
1.2.2、多个元素使用相同的accesskey属性值会怎样
例如,有如下所示的HTML代码:
在Chrome浏览器下,第一行<div>元素的accesskey属性设置会被忽略,第二行的文字变成了红色,说明accesskey属性被重复设置的表现以后面的元素为准。
而在Firefox浏览器下则两败俱伤,两行<div>元素进行快捷键匹配的时候都不能触发点击事件,变成了单纯的focus索引,出现了聚焦虚框,如图所示,行为表现和IE浏览器一致。
从上面的测试结果可以看出,设置多个元素的accesskey属性值相同是一个比较严重的问题,应当极力避免。
1.2.3、约定俗成的开发策略
accesskey属性的默认访问策略并不实用,一是用户并不知道开发者在页面中藏了哪些快捷访问按键,二是组合快捷键的学习成本实在太高,因此,经过多年的发展,业界已经有了一些约定俗成的开发策略。
- 其一,舍弃组合快捷键,使用JavaScript进行一些自定义,让用户通过直接按下accesskey属性对应的值就可以访问对应的链接,或者激活对应的元素。
- 其二,对某些操作的快捷键进行约定俗成的定义,其中最著名、影响最广的就是所有的搜索输入框都使用斜杠字符“/”作为访问快捷键,下图所示的就是脸书网站的accesskey属性设置。
不仅是脸书,像推特、MDN文档都使用“/”作为访问快捷键,也就是你只要按下“/”键,搜索框就会自动聚焦。
我认为国内的网站也都可以使用此策略,其他的快捷键先不要管,就把搜索匹配用上,只要大家都这么使用,就会形成约定俗成的规范,对于用户而言,其实是一件好事。
2、屏幕阅读无障碍访问
在美国,如果网页无法让残疾人正常访问,企业可能会被要求重新设计网站,不仅涉及金钱损失,还要支付律师费。国内前几年也出台了老龄化适配政策,随后也出现了专门支持无障碍访问的公司,可见对无障碍访问的学习还是很有必要的,不求精通,至少要掌握一些基础知识。
2.1、尽可能使用原生元素和属性
目前,国内的Web产品对无障碍访问的支持比较糟糕,以至于,你只要在网页开发的时候,尽可能使用语义精准的HTML元素,所实现的产品的无障碍访问能力已经超过绝大多数公司的产品。
下面介绍关于Web无障碍访问的一些常识性的知识,因为很多人在这方面容易犯错。
有些按钮没有文字,仅有一个小图标,很多人图省事就使用一个<span>元素,这是糟糕的做法。外面一定要嵌套<button>按钮,如果是链接,则可以使用<a>元素。
桌面端网页有很多鼠标悬停显示下拉列表的效果,这些效果一定要同时设置点击切换显隐,否则触摸屏、屏幕阅读器或者键盘访问就会有障碍。
弹出层或者弹出框一定要有关闭按钮,按钮最好位于右上角,如果这和视觉设计稿不符,可以设置透明度为0,这对视力有障碍的用户会非常友好。
所有的控件元素在聚焦的时候一定要有样式变化,建议使用outline添加轮廓,下面这样的CSS设置务必避免:
图形化的内容一定要设置文字内容,尤其是图标内容。以VoiceOver为例(iOS、MAC OS X操作系统内置屏幕阅读器),下面的HTML代码是指不会有任何内容被朗读的(只有按钮元素的title属性值会被朗读):
但如果里面有文字,那么视力有障碍的用户就能识别,例如:只要不是无关紧要的装饰性图片,一定要使用<img>元素,且一定设置alt描述值。如果图片有角标等信息,则可以一并写在alt属性中,比如图所示的角标效果,就可以使用如下所示的HTML代码:
不要单独使用SVG图像,因为哪怕设置<title>元素,也是不会被读取的,例如:
其中的“返回”二字并不会被读取,而是会朗读宽泛的“图像”。不过如果外面有链接元素,则在VoiceOver下就会朗读“返回,链接”,因为此时SVG从图像性质变成了链接内容。
或者使用下一节介绍的ARIA规范属性aria-label:
此时,辅助设备就会朗读“图像 返回”。大家也可以开启VoiceOver(亦叫“旁白”)试试,想象自己是一个视力有障碍的用户,测试一下自己目前所开发的Web页面能不能正常体验,这是提升自己无障碍访问开发能力最快速的方法。
2.2、系统介绍ARIA规范属性
除原生的HTML元素和属性外,我们还可以用ARIA规范定义的HTML属性增强Web产品的无障碍访问能力。从功能上区分,这些属性可以分为三类:ARIA role属性、ARIA常规属性和ARIA状态属性,下面逐一进行介绍。
2.2.1、ARIA role属性
ARIA role属性可以让没有语义的HTML元素变成有语义的元素,例如:
此时,屏幕阅读器就会朗读“按钮 点击”,而不仅仅是“点击”,提升了无障碍访问能力。常用的role属性值包括下面这些。
- role=“alert”
表示警告,例如Ajax数据请求失败在页面顶部或中间位置出现的提示信息: - role=“alertdialog”
表示弹出对话框,和role="alert"的区别在于,alertdialog会有“关闭”或“确定”按钮,例如:
视觉表现见下图。 - role=“application”
表示应用,例如自定义的时间选择器。 - role=“banner”
表示网站的头部,代替<header>元素的角色,一个页面只能有一个,例如: - role=“button”
表示按钮,很好理解,例如:
此role属性值多和tabindex="0"一起使用,保证键盘的无障碍访问。 - 网格与表格
与网格和表格相关的role属性值较多,因此将它们放在一起介绍,详见下表。
下面这段代码示意了以上部分role属性值的使用: - role=“checkbox”
表示复选框,例如:
视觉表现如图所示。 - role=“combobox”
表示下拉列表组合框,例如:
视觉表现如图所示。 - role=“command”
表示指令,纯操作提示,没有任何输入行为,例如富文本编辑器中的Ctrl+Z组合键为撤销、Ctrl+B组合键为加粗、Ctrl+C组合键为复制等。 - role=“complementary”
表示补充内容,类似于<aside>元素,例如: - role=“contentinfo”
表示网站的页脚,类似于<footer>元素,例如: - role=“definition”
表示定义,类似于<dfn>元素,例如: - role=“dialog”
表示对话框,类似于元素,多用在自定义对话框组件上。 - role=“document”
表示复杂小部件或应用程序中的可聚焦内容,多为描述信息,辅助设备可上下切换阅读。例如: - 文档结构角色
有不少role属性值可用来表述文档结构信息,各个属性值、描述及对应的语义化元素如表所示。 - role=“feed”
表示订阅源列表的容器,订阅作品需要支持通过上下键进行定位浏览,例如: - role=“form”
表示表单,类似于<form>元素。 - role=“generic”
表示通用的元素,也就是没有语义的元素,类似于<div>、<span>元素。 - role=“group”
表示分组,例如: - role=“input”
表示允许输入的小部件。 - role=“landmark”
表示标识,例如在摘要中扮演导航的角色,以方便辅助设备快速导航和查找内容。 - role=“link”
表示链接,例如: - role=“log”
表示日志记录,例如: - role=“main”
表示主要内容,类似于<main>元素。 - role=“marquee”
表示滚动内容,例如直播时出现的滚动信息列表: - role=“math”
表示数学公式,例如:
此值多用在MathML语言无法使用的场景下。 - menu系列
菜单栏、菜单、菜单子项均有对应的role属性值,详见下表。
代码表示使用示意:
视觉表现如图所示。 - role=“navigation”
表示导航,类似于<nav>元素。 - role=“none”
role="presentation"的另外一种写法。 - role=“note”
表示注释,类似于<blockquote>元素。 - role=“option”
表示listbox的可选项,例如:
如果给内容设置role=“option”,则辅助设备会将里面的内容作为一个整体进行一次性阅读。 - role=“presentation”
用在让原来有语义的元素变得没有语义的场景下,例如:
等同于: - role=“progressbar”
表示进度条,例如:
视觉表现如图所示。 - role="radio"和role=“radiogroup”
表示单选按钮和单选按钮组,例如:
视觉表现如图所示。 - role=“range”
表示范围,想要自定义类似<meter>、<progress>及<input/range>元素的时候可以考虑此值。 - role=“region”
表示区域,类似于<section>元素。 - role=“scrollbar”
表示可触摸滚动条,例如<input/range>元素的滚动条。 - role=“search”
表示搜索,类似于<search>元素。例如:
建议使用: - role=“searchbox”
表示搜索输入框,类似于<input epub:type=“search”>元素。例如: - role=“select”
表示选择,类似于<select>元素。 - role=“separator”
表示分隔符,类似于<hr>元素。 - role=“slider”
表示滑块,类似于<input epub:type=“range”>元素。 - role=“spinbutton”
表示可上下微调的范围选择元素,<inputepub:type=“number”>元素就符合这个特质,例如鼠标经过数值输入框的时候,右侧会显现上下数值调整按钮,如图所示。 - role=“structure”
表示结构化的文档元素。 - role=“suggestion”
表示建议内容,推荐使用<ins>元素和<del>元素。 - role=“switch”
表示开关,例如:
视觉表现如图所示。 - tab选项卡系列
选项卡、选项卡列表、选项卡面板均有对应的role属性值,详见下表。
例如:
视觉表现如图所示。 - role=“textbox”
表示文本输入框,类似于<input epub:type=“text”>元素,如果同时设置了aria-multiline=“true”,则等同于<textarea>元素。 - role=“timer”
表示计时器,例如: - role=“toolbar”
表示工具栏,用在一系列操作按钮的容器元素上。 - role=“tooltip”
表示悬浮提示元素。 - role=“widget”
表示小部件。 - role=“window”
表示窗口。
2.2.2、ARIA常规属性
ARIA常规属性有很多,这里只介绍一些常用的。
- aria-activedescendant
表示当前活动的子元素,值为后代元素的id值,例如:
在此HTML示例中,aria-activedescendant属性定义了当工具栏获取焦点时,工具栏的第一个控件(拥有id=“button1”)是能获取焦点的子控件。 - aria-atomic
表示区域内容是否完整播报,值可以为true和false。当其值为true时,表示辅助设备需要把整个区域内容都通报给使用者;当其值为false时,则表示只需要通报修改的部分。例如:
这里的aria-atomic属性值为true则表示当时间改变的时候,这里的年月日要完整播报,不要只改了月份就只报月份的内容。 - aria-autocomplete
表示用户文本框的自动提示是否提供。可选值有inline、list、both和none。
需要注意的是,如果aria-autocomplete="list/inline/both"被设置在支持autocomplete的元素上,则autocomplete属性值需要被设置为“on”;如果是aria-autocomplete=“none”,则需要被设置为“off”。
aria-busy
表示当前区域的忙碌状态,默认为false,表示清除busy状态;可选为true,表示该区域正在加载;或为error,表示该区域验证无效。aria-controls
表示当前区域控制的元素,值为目标元素的id值,例如:aria-current
表示当前元素的状态,可选值有page、step、location、date、time、true和false。aria-description
表示当前区域的描述信息,值为描述内容,例如:
该属性类似于aria-label,不过aria-label描述的是简单的信息,aria-description描述的是长句子。aria-describedby
表示当前区域的描述信息,值为描述信息元素的id值,例如:
如果值指定了不止一个id,那么所有元素会合并在一起共同创建一条单独的描述。aria-details
表示当前区域更加详细的描述信息。aria-describedby描述的是纯文本,而aria-details描述的信息可以有图片,甚至包括交互内容。aria-flowto
表示当前区域的下一个区域,值为目标元素的id值。aria-haspopup
表示当前区域是否有弹出框,支持多种属性值,具体如下。- false:没有弹出框。
- true:有弹出框,语义同menu属性值。
- menu:有弹出菜单。
- listbox:有弹出列表。
- tree:有弹出树形结构。
- grid:有弹出表格。
- dialog:有弹出对话框。
此属性比较常用,因为在目前的Web开发中,自定义弹出框、弹出层组件还是非常常见的。
aria-keyshortcuts
表示当前区域的快捷键,值为快捷键内容,例如:aria-label
表示交互元素的描述信息,值为描述内容,例如:aria-labelledby
表示交互元素的描述信息,值为描述信息元素的id值,例如:aria-level
表示当前区域的等级或层级,值为数字。aria-level常用在role="heading"元素上,表示对应的标题的级别,例如:
语义上等同于元素<h3>。aria-live
表示当前区域的内容是否需要实时更新,值为off(默认值)、polite和assertive。当值为polite时,表示在内容更新时,辅助设备会在用户操作完成后播报;当值为assertive时,表示在内容更新时,辅助设备会立即播报。
aria-modal
表示当前区域是否是模态对话框(有半透明黑色背景),值为true或false。aria-multiline
表示当前区域是否支持多行文本,值为true或false。aria-multiselectable
表示当前区域是否支持多选,值为true或false。aria-orientation
表示当前区域的方向,值为horizontal或vertical。aria-owns
表示当前区域拥有的元素,值为目标元素的id值。aria-placeholder
表示当前区域的占位符,值为占位符内容。aria-posinset
表示当前区域在父元素中的位置,值为数字。aria-relevant
表示当前区域的内容更新方式,值为additions、removals、text、all(等同于additions removals text)和additions text(默认值)。
其中各个值的含义如下。- additions:表示添加内容时播报。
- removals:表示删除内容时播报。
- text:表示内容改变时播报。
- all:表示添加、删除、改变内容时都播报。
aria-roledescription
表示定义当前元素角色信息的描述,值为角色描述内容。例如:
此时,屏幕阅读器会阅读“季度报告 幻灯片”,而不是“季度报告 文章”。aria-setsize
表示当前区域的子元素个数,值为数字。例如:aria-sort
表示当前区域的排序方式,值为ascending、descending、none(默认值)和other。aria-valuemax、aria-valuemin和aria-valuenow
表示当前区域的最大值、最小值和当前值,值为数字。对应元素输入框元素的max、min和value属性,使用示意:aria-valuetext
aria-valuetext属性的作用是设置比aria-valuenow属性更为友好的文本朗读内容。例如在自定义下拉列表中,aria-valuenow往往是选项的id,但是朗读的内容应该是对应的文本描述,此时就可以使用aria-valuetext。
2.2.3、ARIA状态属性
ARIA状态属性也有很多,其数组值都是true或false,常用的状态属性参见下表。