我正在参加「掘金·启航计划」
什么是Web无障碍
Web无障碍(Web Accessibility,取头尾各一个字母,总共有11个字母,因此简称a11y),也叫可访问性等。是指在 Web 开发无障碍性意味着使尽可能多的人能够使用 Web 站点,包括那些有视觉、听觉、运动和认知障碍的人。Web无障碍的目的是促进公平、包容和可持续的数字社会发展,让尽可能多的人都能够充分地参与到数字经济和社交生活中。
通过实现Web无障碍,不仅可以提高网站的可用性和用户体验,也能够促进社会的包容性和公正性,让更多人都能够享受数字经济和社交生活带来的便利和快乐。无障碍的实现不只是为了服务残疾人,而是为每个人提供更好的用户体验,并促进商业和社会应用程序的包容性和可持续性。
我们在平时浏览的网页中可能比较少见到无障碍访问技术,而在政府等官方网页中就比较常见,比如国家政务服务平台在header栏就有无障碍浏览的选项,一点击屏幕阅读器就会提示你:“进入国家政务服务平台页面”并出现各种辅助选项:
相关法律
在一些国家已经对Web无障碍进行了立法,如欧盟的EN 301 549等。在W3C网页无障碍法律和政策的列表中,我们国家的相关的法律政策有:
实际开发中主要考虑的残疾类型
根据世界卫生组织2022年发布的最新数据,全球不同类型残疾的人数估计如下:
- 身体残疾:全球约7.5%的人口有身体残疾,包括肢体缺陷、肌肉萎缩、截肢等。
- 智力残疾:全球约有1%-3%的人口患有智力残疾,包括低智商、认知障碍等。
- 精神残疾:全球约有10%-20%的人口患有精神残疾,包括精神分裂症、情绪障碍、焦虑症等。
- 视力障碍:全球约2.2%的人口患有视力障碍,包括近视、远视、色盲、失明等。
- 听力障碍:全球约5%的人口有听力障碍,包括耳聋、听力损失等。
另外截止到2020年,我们国家中国残疾人总人数为8592.8万人,60岁以上老年人口约为2.68亿人,占全国总人口比例为19.14%。注意这些数据估计只是近似值,可能不完全准确,可能存在一个人患有多种障碍疾病等其他情况。此外,世界不同区域在残疾统计报告的定义和方法上可能存在差异。
在实际开发中,由于不同的应用功能及使用场景等因素,我们不可能完全覆盖所有人群,尽自己最大的能力让尽可能多的人能够无障碍的使用自己的应用就好,因此在开发中,我们主要考虑以下几种残疾类型:
- 视觉障碍:包括完全失明、低视力和色盲等。为视觉障碍用户提供无障碍体验的方法包括使用文字替代图像、确保足够的对比度、提供音频描述和键盘导航等。
- 听力障碍:包括完全失聪和听力受损。为听力障碍用户提供无障碍体验的方法包括提供字幕和文本版本、使用符号或图像表示声音信息,以及提供分离式听力设备等。
- 运动障碍:包括手部、腕部和手臂运动受限,以及其他身体上的运动障碍。为运动障碍用户提供无障碍体验的方法包括提供可访问的表单设计(如避免过多的鼠标操作)、提供大型按钮、为语音输入提供支持等。
- 认知障碍:包括学习困难、记忆问题、集中注意力困难等。为认知障碍用户提供无障碍体验的方法包括提供易于理解和简单的语言、使用一致的布局和导航结构、提供可选项等。
当然还需要考虑其它的残疾类型,根据自己的实际开发的应用程序的用途,了解哪些用户会使用到自己的应用,还要考虑用户的年龄、文化差异或意识形态差异而遇到困难等因素来创建更加包容的设计。
WCAG
WCAG(Web内容无障碍性指南)2.0和2.1是由万维网联盟(W3C)发布的一系列规范,旨在确保Web内容对所有人都可访问,包括残疾人。能帮助Web开发者创建尽可能无障碍的内容,从而使得更多的人能够方便地使用Web。
WCAG 2.0于2008年发布,共有12个准则,每个准则都有一些成功标准,以帮助开发者创建无障碍的Web内容。其中每个成功标准都有三个级别:A、AA和AAA,级别A为最基本的要求,级别AAA为最高的要求。建议网站和应用程序至少达到AA级别的无障碍要求。因为AA级别的要求已经包括了许多基本的无障碍功能,可以满足大多数用户的需求。但是,如果网站或应用程序的用户群体中有许多有特殊需求的人,例如视觉障碍者或听力障碍者,那么建议尽可能达到AAA级别的无障碍要求,以确保他们能够完全访问网站的内容。
WCAG 2.1是WCAG 2.0的扩展版本,于2018年发布。它新增了13个成功标准,其中包括一些针对移动设备、低视力用户和认知障碍用户的要求。这些新的成功标准可以帮助开发者进一步提高Web内容的无障碍性。
MDN推荐实际开发中WCAG版本的选择:WCAG 2.1是最新且相关的可访问性标准。使用WCAG 2.1帮助更多残障人士,降低网站所有者未来的法律风险。分配资源时首先以 WCAG 2.0 为目标。然后升级到WCAG 2.1。
主要内容(WCAG 2.0)
主要包括四项原则、十二个准则和60+个成功标准。具体的内容可以查看百度百科WCAG。这里介绍一下WCAG 2.1新增17项成功标准:
- 1.3.4 Orientation:要求网页必须支持横屏和竖屏两种方向,并且在切换方向时,网页的内容应该自动适应屏幕的方向。除非需要特定的显示方向。
- 1.3.5 Identify Input Purpose(AA):网页中的输入框必须明确标识出其用途,例如登录、搜索等,以便于用户正确地输入信息。
- 1.3.6 Identify Purpose(AAA):可以通过编程方式确定网页中的=元素的用途,例如按钮、链接等,以便于用户正确地使用网页。
- 1.4.11 Non-Text Contrast(AA):网页中的用户界面组件和图形的视觉呈现与相邻颜色的对比度至少为3 : 1。
- 1.4.12 Text Spacing(AA):网页中的文本必须有足够的间距,以便于用户能够清晰地看到每个字符和单词。
- 1.4.13 Content on Hover or Focus(AA):网页中的内容在鼠标悬停或者焦点聚焦时必须有明显的反馈,以便于用户知道自己正在与哪个元素交互。
- 2.1.4 Character Key Shortcuts(A):网页中的快捷键必须与键盘上的字符键相对应,以便于用户能够轻松地使用快捷键。
- 2.2.6 Timeouts(AAA):网页中的超时时间必须足够长,以便于用户能够完成操作,同时也要提供足够的提示,以便于用户知道自己需要在多长时间内完成操作。除非在用户不执行任何操作时将数据保留20小时以上。
- 2.3.3 Animation from Interactions(AAA):由交互触发的动态动画可以被禁用,除非该动画对所传达的功能或信息至关重要。
- 2.5.1 Pointer Gestures(A):如果一个功能可以用单个指针来操作,而不需要使用多点触控或基于路径的手势,那么就应该尽量避免使用这些手势。只有在必要的情况下才应该使用多点触控或基于路径的手势。这样可以提高用户的操作效率和体验,避免不必要的复杂性和混淆。
- 2.5.2 Pointer Cancellation(A):在使用多点触控或基于路径的手势时,如果用户不想继续操作,可以通过取消手势来中止操作。
- 2.5.3 Label in Name(A):网页中的标签必须包含在元素的
name
中,以便于屏幕阅读器等辅助技术能够正确地读取标签。 - 2.5.4 Motion Actuation(A):可以由设备运动或用户运动操作的功能也可以由用户界面组件操作,并且可以禁用响应运动以防止意外驱动。
- 2.5.5 Target Size(AAA):
- 2.5.6 Concurrent Input Mechanisms(AAA):Web内容不限制平台上可用输入模式的使用,除非这种限制是必要的,需要确保内容的安全性,或者需要尊重用户设置。
- 4.1.3 Status Messages(AA):在使用标记语言实现的内容中,可以通过角色或属性以编程方式确定状态消息,这样就可以通过辅助技术将它们呈现给用户,而无需接收焦点。
更多内容请查阅Web Content Accessibility Guidelines (WCAG) 2.1。
三剑客和无障碍
HTML无障碍主要体现在语义元素的正确使用和部分属性上,现在绝大部分HTML元素已经具有或者新增了特定的语义。比如<i>
,早期的<i>
元素就表示以斜体表示文本。而现在<i>
元素在保留了斜体效果的基础上,新增表示由于某种原因需要与普通文本区别开来的文本范围的语义。
了解了各元素的语义,有时还要将元素与一些属性结合使用,以增强元素的语义,保证辅助技术能够正确识别。比如像<table>
元素,当表格比较复杂(比如内嵌表格,多个<tbody>
等)时,很难向用户解释清楚表格的内容。这有两种解决方式:
- 将大的复杂的表格细分成小的直观的表格。
- 如果无法拆分,可以在
<td>
元素上使用id
和headers
属性的组合,以编程方式将每个表单元格与与该单元格关联的标头相关联。
同样的还有<img>
等多媒体元素,我们无法保证图片等多媒体能够成功加载,因此我们通常会结合alt
属性,当多媒体内容加载失败时,有替代文本提示用户以及供辅助技术使用。
其他元素的语义和属性之前的文章中有说明,这里不过多赘述。
WAI-ARIA
在HTML5引入许多语义化元素之前,开发人员可能会用<div>
等元素和一些属性来模拟语义。比如用<div>
和具有命名规范的class
或id
等属性来模拟<dialog>
元素(由于现在<dialog>
的兼容性还不是特别好,现在很多网页和组件库中的对话框仍是由<div>
模拟而成)。
另外对于一些复杂的控件,可能还会用到一些JavaScript库来实现,这些库会动态生成一系列嵌套的<div>
,并对其进行控制。虽然直观上看有效果,但是一些辅助技术无法理解它们。
HTML5引入了对WAI-ARIA的官方支持,并提供了新的语义元素和属性来支持无障碍访问。WAI-ARIA(Web Accessibility Initiative – Accessible Rich Internet Applications)是一项由国际标准化组织(ISO)和W3C共同制定的技术规范,旨在提高Web应用程序的无障碍访问能力。它定义了一组属性和角色,这些属性和角色可以在HTML、SVG和其他Web技术中使用,以帮助屏幕阅读器和其他辅助技术理解Web内容的结构、功能和交互。对于通过动态内容或用户界面控件改变而不是在页面加载时进行完全渲染的应用程序尤其有用。
其中,WAI-ARIA的核心之一是ARIA(Accessible Rich Internet Applications)。它是一种Web技术,允许Web开发人员向HTML元素添加额外的语义信息,以便于使残障用户能够更好地使用Web应用程序。还为Web开发人员提供了一种简单的方式来改善Web应用程序的可访问性。
注意:
- WAI-ARIA规范还包括其他标准,例如Accessible Name and Description Computation规范和Core Accessibility API Mappings规范。
- 在HTML5之前,WAI-ARIA并不是HTML标准的一部分。但是WAI-ARIA规范仍然可以与早期版本的HTML(如HTML4和XHTML1)一起使用。
- 如果有合适的语义化元素,就优先使用语义化元素,语义化元素(如
<main>
)已经内置了相应的无障碍技术。当元素无法转递必要的信息、需要增强语义或者需要通过JavaScript动态生成复杂的元素等情况才考虑这些辅助技术。
主要内容(WAI-ARIA 1.2)
WAI-ARIA规范的主要内容有:
- 角色(Role):指定元素在用户界面中扮演的角色,以便辅助技术可以正确地处理它们。
- 属性(Properties):提供有关元素的其他信息,例如状态、值或描述。
- 状态(State):指定元素的状态,例如聚焦、禁用、展开等。
角色(Role)
WAI-ARIA 角色模型定义了一组角色(Roles)来描述Web界面中的元素,帮助用户理解其功能和交互方式。每个角色都具有一组属性和方法,被用来描述相关元素的状态、行为和外观。这些属性和方法有助于开发人员创建可访问的Web应用程序。通过使用正确的角色,可以使屏幕阅读器和其他辅助技术更好地理解页面结构,并提高应用程序的可访问性。
在HTML中,元素的角色由role
属性指定。虽然role
属性可以应用于任何HTML元素,但通常应该仅用于不具备明确含义的元素(例如<div>
或<span>
),或者当元素使用方式与其默认行为不同时。
注意:使用
role
属性并不意味着我们可以忽略HTML的基本结构和语义。仅应该在需要进行语义增强的情况下使用,而不是将其作为一种浏览器样式的替代方法。
不同的角色之间,可能会存在超类或子类的关系,如果你熟悉JavaScript的类就很好理解了。WAI-ARIA规范中定义了近100个不同的角色,这些角色可以被分为以下几种类别(角色具有的必需、支持、继承和禁止的状态/属性请看后文状态与属性部分)。
Abstract Roles
抽象角色表示为网页语义而设计的一组无形的角色,描述了用户界面中的结构和行为。
角色 | 描述 | 超类 | 子类 |
---|---|---|---|
command | 表示一个可执行任务或命令的UI组件。通常是按钮、菜单项或链接等,用户可以通过某种方式(如鼠标点击或键盘事件)与它们进行交互。 | widget | button 、link 和menuitem |
composite | 表示具有子元素的容器或部件,其本身不会提供任何功能。该角色通常与其他角色一起使用来定义自定义控件,例如树形视图或面板。 | widget | grid 、select 、spinbutton 和tablist |
input | 允许用户输入的通用类型的小部件。 | widget | checkbox 、combobox 、option 、radio 、slider 、spinbutton 和textbox |
landmark | 表示在页面布局中起到重要结构作用的元素。例如页眉、页脚、导航栏、主内容区等。 | section | banner 、complementary 、contentinfo 、form 、main 、navigation 、region 和search |
range | 表示可调整值的控件,例如滑块、数字输入框和时间选择器等。 | structure | meter 、progressbar 、scrollbar 、slider 和spinbutton |
roletype | 是所有其他角色继承的基角色。,它没有具体实例,只是作为其它角色的基础,用来定义角色的通用属性和方法。 | - | structure 、widget 和window |
section | 用于标识一个文档或应用程序中的逻辑区域或部分。 | structure | alert 、blockquote 、caption 、cell 、code 、definition 、deletion 、emphasis 、figure 、group 、img 、insertion 、landmark 、list 、listitem 、log 、marquee 、math 、note 、paragraph 、status 、strong 、subscript 、superscript 、table 、tabpanel 、term 、time 和tooltip |
sectionhead | 表示网页中的标题或其他类型的区域,这些区域可以作为文档结构的一部分,并提供上下文信息。通常与具有相似功能的HTML元素(如<h1> ~<h6> )结合使用。 | structure | columnheader 、heading 、rowheader 和tab |
select | 允许用户从一组选项中进行选择的表单小部件。 | composite 和group | listbox 、menu 、radiogroup 和tree |
structure | 表示文档结构和组织。这些角色通常用于更好地描述Web页面上的用户界面元素。 | roletype | application 、document 、generic 、presentation 、range 、rowgroup 、section 、sectionhead 和separator |
widget | 定义为独立的用户界面控件,不属于其他角色的子类。该角色可以包含交互性质、支持键盘操作和通过程序访问等的特性。widget 角色可以分为两类:1. 可输入的 widget ,如文本框、下拉框、滑块等;2. 可操作的 widget ,如按钮、菜单、标签页等。 | roletype | command 、composite 、gridcell 、input 、progressbar 、row 、scrollbar 、separator 和tab |
window | 表示浏览器或应用程序窗口。,图形用户界面(GUI)上下文中具有类似窗口的行为,无论它们是作为操作系统中的本机窗口实现,还是仅仅作为文档的一部分样式看起来像窗口。 | roletype | dialog |
注意:抽象角色只是用来构建具体角色的概念模型,它们缺乏明确的语义含义,可能会导致混淆和错误的解释,因此不能被直接用于描述Web内容。
Widget Roles
部件角色代表用户界面中的交互性控件或部件。例如,按钮、滑块、文本框、下拉菜单等。
角色 | 描述 | 超类 | 子类 |
---|---|---|---|
button | 当单击或按下时,允许用户触发操作的输入。 | command | - |
checkbox | 代表一个复选框控件,用户可以通过选择或取消选择该控件来表示其意图。 | input | switch |
gridcell | 网格或树状网格中的单元格。 | cell 和widget | columnheader 和rowheader |
link | 对内部或外部资源的交互式引用,当激活该引用时,会导致用户代理导航到该资源。 | command | - |
menuitem | 表示菜单或菜单栏中包含的一组选项中的一个选项。 | command | menuitemcheckbox |
menuitemcheckbox | 表示具有可检查状态的菜单项。 | command | menuitemradio |
menuitemradio | 具有相同角色的一组元素中的可选择菜单项,一次只能选中其中一个。 | menuitemcheckbox | - |
option | 列表框中的可选择项。 | input | treeitem |
progressbar | 表示耗时较长的任务进度状态的元素。 | range 和widget | - |
radio | 具有相同角色的一组元素中的可选中输入,一次只能选中其中一个。 | input | - |
scrollbar | 表示控制查看区域内内容的滚动,而不管内容是否在查看区域内完全显示的图形对象。 | range 和widget | - |
searchbox | 表示用于指定搜索条件的一种文本框。 | textbox | - |
separator (可聚焦时) | 表示一种分隔和区分部分内容或一组菜单项的分隔符。 | structure (不可聚焦)和widget (可聚焦) | - |
slider | 表示用户可以从给定范围内选择一个值的输入。 | input 和range | - |
spinbutton | 表示一种用户界面控件,该控件允许用户通过按上下箭头或滑动条来增加或减少值。 | composite 、input 和range | - |
switch | 一种复选框类型,表示开/关值,而不是选中/未选中的值。 | checkbox | - |
tab | 表示一个分组标签,它提供了一种机制,用于选择要呈现给用户的选项卡内容。 | sectionhead 和widget | - |
tabpanel | 与选项卡关联的资源的容器,其中每个选项卡都包含在一个表中。 | section | - |
textbox | 一种允许自由格式文本作为其值的输入类型。 | input | searchbox |
treeitem | 树的选项项。这是树中的一个元素,如果它包含树项元素的子级组,则可以展开或折叠。 | listitem 和option | - |
combobox | 表示一个具有可编辑文本框和下拉列表的交互控件。下拉列表中包含与当前输入值匹配的预选项,用户可以通过键盘或鼠标选择其中一个选项。 | input | |
grid | 表示网页上的网格组件,通常包含多个单元格和表头单元格。这些单元格可以在行和列之间进行导航,并且可以选择一个或多个单元格。 | composite 和table | treegrid |
listbox | 表示允许用户从选项列表中选择一个或多个项目的小部件。 | select | - |
menu | 表示一种为用户提供选项列表的小部件。 | select | menubar |
menubar | 菜单的一种显示方式,通常保持可见,并且通常水平显示。 | menu | - |
radiogroup | 表示一组单选按钮。 | select | - |
tablist | 选项卡元素的列表,是对tabpanel 元素的引用。 | composite | - |
tree | 表示允许用户从分层组织的集合中选择一个或多个项目的小部件。 | select | treegrid |
treegrid | 表示一种具有层次结构和网格布局的表格控件。 | grid 和tree | row 和rowgroup (包含row ) |
注意:
- 从倒数第9(包含)开始的角色充当复合用户界面小部件,这些角色通常充当管理其他包含小部件的容器。
- 如果按下链接触发一个操作,但不会改变浏览器焦点或页面位置,建议考虑使用
button
角色而不是link
角色。
Document Structure Roles
文档结构角色是一组用于描述网页结构,通常不是交互式的。
角色 | 描述 | 超类 | 子类 |
---|---|---|---|
application | 包含一个或多个需要用户输入(如键盘或手势事件)的可聚焦元素的结构,这些元素不遵循Widget Roles支持的标准交互模式。 | structure | - |
article | 表示一个独立的、完整的、可以被独立于上下文阅读的内容块。如独立的文章、博客帖子、新闻报道等。 | document | - |
blockquote | 表示从其他来源引用的一段内容。 | section | - |
caption | 标识与某个特定区域相关联的标题或说明。它通常用于表格、图像、音频和视频等媒体元素。 | section | - |
cell | 表格容器中的单元格。 | section | columnheader 、gridcell 和rowheader |
columnheader | 表示包含列标题信息的单元格。 | cell 、gridcell 和sectionhead | - |
definition | 表示一个术语或概念的定义。 | section | - |
deletion | 表示包含标记为已删除的内容或建议删除的内容。 | section | - |
document | 包含使用辅助技术的用户可能希望在阅读模式下浏览的内容的元素。 | structure | article |
emphasis | 表示一个或多个强调的字符。参考HTML <em> 元素。 | section | - |
feed | 表示一个可滚动的文章列表,滚动可以使文章添加到列表的任意一端或从列表的任意一端删除。 | list | - |
figure | 表示可感知的内容部分,通常包含图形文档、图像、代码片段或示例文本。图形的部分可能是用户可导航的。 | section | - |
generic | 表示一种本身没有语义的无名容器元素,但需要一些交互性质。参考HTML <div> 和<span> 。 | structure | - |
group | 表示一组用户界面对象,不打算通过辅助技术包含在页面摘要或目录中。与下文的region 角色相反。 | section | row 、select 和toolbar |
heading | 表示页面某一部分的标题。参考<h1> ~<h6> 。 | sectionhead | - |
img | 表示构成图像的元素集合的容器。可以包含标题和描述性文本,以及多个图像文件,这些文件在一起查看时给人的印象是单个图像。 | section | - |
insertion | 表示插入包含标记为已添加的内容或建议添加的内容。 | section | - |
list | 表示包含列表项元素的部分。 | section | directory 和feed |
listitem | 表示一个列表中的一个项目或目录。 | section | treeitem |
math | 表示数学表达式的内容。参考MATHML。 | section | - |
meter | 表示已知范围内的标量测量值或分数值的元素。 | range | - |
none | 表示一个元素不具有任何语义信息,可以被屏幕阅读器忽略,常用于纯装饰性的元素(如分割线),或者当一个元素的作用已经通过其他方式明确表达时(如<div> 元素)。使用none 角色可以减少屏幕阅读器提示无关内容,从而提高可访问性。 | - | - |
note | 表示其内容是资源的主要内容的插入或辅助部分。 | section | - |
paragraph | 表示一个段落。 | section | - |
presentation | 表示一个元素虽然有语义信息,但是其目的是在视觉上呈现特定外观,而不是向用户传达任何语义。 | structure | - |
row | 表示表格容器中的一行单元格。 | group 和widget | - |
rowgroup | 表示在表格容器中包含一个或多个行元素的结构。 | structure | - |
rowheader | 表示包含一行标题信息的单元格。 | cell 、gridcell 和sectionhead | - |
separator (不可聚焦时) | 表示一种分隔和区分部分内容或一组菜单项的分隔符。 | structure (不可聚焦时)和widget (可聚焦时) | - |
strong | 表示重要、严肃或紧急的内容参见相关的强调。 | section | - |
subscript | 表示一个或多个下标字符。 | section | - |
superscript | 表示一个或多个上标字符。 | section | - |
table | 表示包含按行和列排列的数据的部分。 | section | grid |
term | 表示有相应定义的词或短语。 | section | - |
time | 表示特定时间点的元素。 | section | - |
toolbar | 表示以紧凑的视觉形式表示的常用功能按钮或控件的集合。 | group | - |
tooltip | 表示显示元素描述的上下文弹出框。 | section | - |
注意:
在WAI-ARIA 1.2规范中
directory
角色已被弃用。关于
generic
角色:当一个元素不属于已有的角色类型(例如button、checkbox等),同时又需要支持一些基本的交互行为(例如聚焦、禁用等),则可以将其设置为
generic
角色。由于
generic
角色缺乏特定的语义,因此在使用时需要结合其他属性和标签来提供更准确的信息。规范中对
none
角色的注意事项:另外如果一个元素既没有语义信息,又要用于视觉呈现,则应该同时使用
none
和presentation
两个角色。例如,一个空白的<div>
元素,它被用于实现网页布局,但是不具有任何语义信息,此时应该同时使用none
和presentation
角色。关于具有time角色元素的文本内容的有效日期或时间相关字符串的示例:
- 有效的月份字符串:2019-11
- 有效日期字符串:2019-11-18
- 有效的无年日期字符串:11-18
- 有效的时间字符串:09:54:39
- 有效的浮动日期和时间字符串:2019-11-18T14:54
- 有效的时区偏移字符串:-08:00
- 有效的全局日期和时间字符串:2019-11-18T14:54Z
- 有效的周字符串:2019-W47
- 四位或四位以上的ASCII数字,至少有一位不是U+0030 DIGIT ZERO (0): 0001
- 有效的持续时间字符串:4h 18m 3s
Landmark Roles
标志性角色用作导航地标的页面区域,所有这些角色都继承自landmark
基类型
角色 | 描述 | 超类 |
---|---|---|
banner | 这个标识包含的大部分内容是面向整个网站而不是某个具体页面的。面向站点的内容通常包括诸如站点赞助商的徽标或标识以及特定于站点的搜索工具之类的内容。 | landmark |
complementary | 用于标记网页内容的角色,表示与周围内容有某种关联但不是必需的辅助性信息。 | landmark |
contentinfo | 用于标识文档中的一个区域,该区域包含有关文档的元信息或版权信息等内容。通常,这个区域位于页面底部,可以包括如"版权所有"、"法律声明"、"联系我们" 等信息。 | landmark |
form | 表示表单元素集合的角色。具有form 角色的元素应该包含一组表单元素,这些元素可以通过标签、属性或其他方式与这个元素相关联。 | landmark |
main | 用于标识页面或应用程序中的主要内容区域。 | landmark |
navigation | 包含导航元素(通常是链接)的集合,用于导航文档或相关文档的地标。 | landmark |
region | 用于标识一个区域,该区域包含其他组件或内容,并且它们在语义上相关联。 | landmark |
search | 一个标志性的区域,包含一系列项目和对象,作为一个整体,组合起来创建一个搜索设施。 | landmark |
Live Region Roles
实时区域角色描述如何将Web应用程序中动态变化的内容与屏幕阅读器进行交互,使得这些内容能够被盲人和视力受限用户等人士正确地感知。
角色 | 描述 | 超类 | 子类 |
---|---|---|---|
alert | 一种活跃的区域,有重要的、通常是时效性很强的信息。 | section | alertdialog |
log | 一种可用于标记日志输出的元素角色。新信息按有意义的顺序添加,旧信息可能消失。 | section | - |
marquee | 用于标识提供循环滚动文本、图像或其他内容的区域。 | section | - |
status | 标识其内容为用户提供咨询信息,但没有重要到需要发出警报,通常(但不一定)以状态栏的形式呈现。 | section | timer |
timer | 表示一个可以在指定的时间间隔内触发事件的计时器。可以被使用在一些需要时间敏感性的场景,比如倒计时或者进度条等。 | status | - |
Window Roles
窗口角色角色充当浏览器或应用程序中的窗口。
角色 | 描述 | 超类 | 子类 |
---|---|---|---|
alertdialog | 一种包含警报消息的对话框类型,其中初始焦点指向对话框中的一个元素。 | alert 和dialog | - |
dialog | 表示一个模态对话框。 | window | alertdialog |
状态/属性
WAI-ARIA提供了一组辅助功能状态/属性,用于支持各种操作系统平台上的平台辅助功能API。属性是控件或组件的持久化特征或配置选项。通常在创建控件或组件时设置,并且在其整个生命周期内保持不变,对应于控件或组件的默认状态;状态是指控件或组件当前所处的状态。两者都提供了关于对象的特定信息,并且都构成了角色性质定义的一部分。在HTML中两者均以aria-
开头,作为HTML的全局属性使用。
注意:状态/属性也存在继承。
值类型
属性和状态的值均可以是以下几种类型:
true
/false
:表示真或假。除非另有指定,否则此值类型的默认值为false
。true
/false
/undefined
:表示值表示true
、false
或undefined
(不适用)。除非另有指定,否则此值类型的默认值是未定义的。- tristate:表示
true
、false
、mixed
或undefined
的值。除非另有指定,否则此值类型的默认值是未定义的。 - ID reference:表示引用同一文档中另一个元素的
id
。 - ID reference list:表示一个或多个
id
引用的列表。 - integer:表示没有分数部分的数值。
- number:表示任何实数值。
- string:不受约束的值类型。
- token:一组有限的允许值之一。
- token list:一个或多个token的列表。
注意:这些是状态/属性的通用类型,但不定义特定的表示。
状态/属性分类
在WAI-ARIA 1.2规范中,状态/属性有40多个,分为下文6类。
注意:规范中有一类Drag-and-Drop Attributes,但是在WAI-ARIA 1.1规范中该类的两个状态/属性就已经被弃用了,因此本文不过多赘述。
有些状态/属性会出现在多个分类的情况,在本文中重复的状态/属性将提出放在一个表格中,如果分类A中的全部状态/属性也都属于其他分类,则会在分类A中说明。
另外各分类名称的缩写是我定义的,方便在重复的状态/属性表中查阅(有水文的成分😝)。
Translatable States and Properties(TSAP)
可翻译状态/属性在页面本地化时应该进行翻译。
属性 | 值类型 | 描述 | 角色使用 | 继承到角色 |
---|---|---|---|---|
aria-label | string | 定义一个标记当前元素的字符串值。 | 基本标记的所有元素,但这些角色除外:caption 、code 、deletion 、emphasis 、generic 、insertion 、paragraph 、presentation 、strong 、subscript 和superscript | - |
aria-placeholder | string | 定义一个简短提示(单词或短语),用于在控件没有值时帮助用户输入数据。提示可以是示例值或预期格式的简要说明。 | textbox | searchbox |
aria-roledescription | string | 为元素的角色定义人类可读的、作者本地化的描述。 | - | - |
aria-valuetext | string | 提供当前值的自定义文本描述。 | range 、separator 和spinbutton | meter 、progressbar 、scrollbar 、slider 和spinbutton |
WAI-ARIA 1.2规范:
- 使用
aria-roledescription
时,作者还应确保:
- 应用
aria-roledescription
的元素具有有效的WAI-ARIA角色或隐式WAI-ARIA角色语义。aria-roledescription
的值不为空或不包含仅空格字符。 如果存在以下任何条件,则用户代理不得公开aria-roledescription
属性:- 应用
aria-roledescription
的元素没有有效的WAI-ARIA角色,或者没有隐式的WAI-ARIA角色语义。- 应用咏叹调角色描述的元素具有显式或隐式的WAI-ARIA角色,其中禁止
aria-roledescription
。aria-roledescription
的值为空或仅包含空格字符。- 作者应仅在呈现的值无法有意义地表示为数字时设置
aria-valuetext
属性。如果设置了aria- valuettext
属性,作者也应该设置aria-valuenow
属性,除非该值是未知的(例如,在不确定的进度条上)。aria-errormessage
在WAI-ARIA 1.2规范中不再属于GSAP。重复状态/属性:
aria-label
也属于GSAP(除非另有禁止)和WA分类。aria-placeholder
、aria-roledescription
和aria-valuetext
也属于WA分类。
Global States and Properties(GSAP)
有些状态/属性适用于所有宿主语言元素,而不管是否应用了角色。除非另有禁止,否则所有角色和所有基本标记元素都支持以下全局状态/属性。如果角色禁止使用任何全局状态或属性,那么这些状态或属性将在定义角色的部分中包含的特征表中被列为禁止的。
状态/属性 | 值类型 | 描述 | 角色使用 |
---|---|---|---|
aria-keyshortcuts | string(以空格分隔) | 指示作者实现的用于激活元素或使元素获得焦点的键盘快捷键。 | 全部角色 |
aria-current | token:page :表示一组页面中的当前页面。step :表示流程中的当前步骤。location :表示环境或上下文中的当前位置。date :表示日期集合中的当前日期。time :表示一组时间中的当前时间。true :表示集合中的当前项。false :不表示集合中的当前项。 | 表示在一组相关元素中的一个元素在视觉上显示它是集合中的当前项。 | 全部角色 |
注意:
- 对于
aria-keyshortcuts
,当指定字母键时,大小写不敏感:a
和A
是相同的。作者应该提供一种公开键盘快捷键的方法,以便所有用户都可以发现它们,
Widget Attribut(WA)
包含GUI系统或富internet应用程序中常见用户界面元素的特定属性,用于支持Widget Roles。
属性 | 值类型 | 描述 | 角色使用 | 继承到角色 |
---|---|---|---|---|
aria-autocomplete | token:inline :当用户提供输入时,建议完成所提供输入的一种方式的文本可能会在插入符号之后动态插入。list :当用户提供输入时,可能会显示一个元素,其中包含可以完成所提供输入的值集合。both : 当用户提供输入时,可能会显示一个元素,其中包含可以完成所提供输入的值集合。如果显示,则会自动选择集合中的一个值,并且完成自动选择的值所需的文本将显示在输入中的插入符号之后。none :当用户提供输入时,不会显示尝试预测用户打算如何完成输入的自动建议。 | 指示输入文本是否会触发对combobox 、searchbox 或textbox 的用户预期值的一个或多个预测的显示,并指定如果进行了预测,将如何显示预测。 | combobox 和textbox | searchbox |
aria-checked | string | 定义一个简短提示(单词或短语),用于在控件没有值时帮助用户输入数据。提示可以是示例值或预期格式的简要说明。 | textbox | searchbox |
aria-disabled | true /false | 指示该元素是可感知的,但是(true )否(false )已禁用,因此不能编辑或以其他方式操作。 | application 、button 、composite 、gridcell 、group 、input 、link 、menuitem 、scrollbar 、separator 和tab | checkbox 、columnheader 、combobox 、grid 、listbox 、menu 、menubar 、menuitemcheckbox 、menuitemradio 、option 、radio 、radiogroup 、row 、rowheader 、searchbox 、select 、slider 、spinbutton 、switch 、tablist 、textbox 、toolbar 、tree 、treegrid 和treeitem |
aria-expanded | true /false /undefined | 指示元素是展开(true )还是折叠(false )。 | application 、button 、checkbox 、combobox 、gridcell 、link 、listbox 、menuitem 、row 、rowheader 、tab 和treeitem | columnheader 、menuitemcheckbox 、menuitemradio 、rowheader 和switch |
aria-haspopup | token:false :指示该元素没有弹出窗口。true 和menu :指示弹出窗口是菜单。listbox :表示弹出框为列表框。tree :表示弹出窗口为tree。grid :表示弹出框为网格。dialog :表示弹出窗口为对话框。 | 定义了一个元素是否拥有下拉列表、菜单或对话框。 | application 、button 、combobox 、gridcell 、link 、menuitem 、slider 、tab 、textbox 和treeitem | columnheader 、menuitemcheckbox 、menuitemradio 、rowheader 和searchbox |
aria-invalid | token:grammar :发现了一个语法错误。spelling :检测到拼写错误。false :中没有检测到错误。true :用户输入的值验证失败。 | 表示用户输入的表单控件的有效性。 | application 、checkbox 、combobox 、gridcell 、listbox 、radiogroup 、slider 、spinbutton 、textbox 和tree | columnheader 、rowheader 、searchbox 、switch 和treegrid |
aria-level | integer | 定义结构中元素的层次结构级别。数值越大层次结构越深。 | heading 、listitem 和row | treeitem |
aria-modal | true /false | 指示一个元素在显示时是(true )否(false )是模态的。 | window | alertdialog 和dialog |
aria-multiline | true /false | 指示文本框是接受多行(true )输入还是只接受单行(false )输入。 | textbox | searchbox |
aria-multiselectable | true /false | 指示用户可以从当前可选择的子项中选择一个(false )/多个(true )项。 | grid 、listbox 、tablist 和tree | treegrid |
aria-orientation | token:horizontal :元素是水平方向的。vertical :元素是垂直定向的。undefined :元素的方向未知/不明确。 | 指示元素的方向是水平、垂直还是未知/不明确。 | scrollbar 、select 、separator 、slider 、tablist 和toolbar | listbox 、menu 、menubar 、radiogroup 、tree 和treegrid |
aria-pressed | tristate:false :元素支持被按下,但当前未按下。true :元素被按下了。undefined :该元素不支持按下。mixed :指示三状态切换按钮的混合模式值。 | 表示一个按钮是否被按下。 | button | - |
aria-readonly | true /false | 指示元素是(true )否(false )只读,不能设置值。 | checkbox 、combobox 、grid 、gridcell 、listbox 、radiogroup 、slider 、spinbutton 和textbox | columnheader 、rowheader 、searchbox 、switch 和treegrid |
aria-required | true /false | 指示一个表单控件是(true)否(false)必填。 | checkbox 、combobox 、gridcell 、listbox 、radiogroup 、spinbutton 、textbox 和tree | columnheader 、rowheader 、searchbox 、switch 和treegrid |
aria-selected | true /false /undefined (元素不能被选中) | 指示各种小部件当前是(true )否(false )被选中。 | gridcell 、option 、row 和tab | columnheader 、rowheader 和treeitem |
aria-sort | token:ascending :项升序排序。descending :项降序排序。none :没有对列应用已定义的排序。other :应用了升序或降序以外的排序算法。 | 指示表或网格中的项是按升序还是降序排序。 | columnheader 和rowheader | - |
aria-valuemax | number | 定义范围小部件允许的最大值。 | range 、scrollbar 、separator 、slider 和spinbutton | meter 、progressbar 、scrollbar 、slider 和spinbutton |
aria-valuemin | number | 定义范围小部件允许的最小值。 | range 、scrollbar 、separator 、slider 和spinbutton | meter 、progressbar 、scrollbar 、slider 和spinbutton |
aria-valuenow | number | 定义范围小部件的当前值。 | range 、scrollbar 、separator 、slider 和spinbutton | meter 、progressbar 、scrollbar 、slider 和spinbutton |
注意:
如果将
aria-autocomplete
设置为list
或both
,必须确保同时满足以下两个条件:
- 该元素具有为
aria-controls
指定的值,该值引用包含建议值集合的元素。- 该元素具有一个
aria-haspopup
值,该值与包含建议值集合的元素的角色匹配。
aria-disabled
、aria-haspopup
和aria-invalid
在WAI-ARIA 1.2规范中不再属于GSAP。虽然目前在
columnheader
、rowheader
和row
上支持aria-disabled
,但官方计划禁止在具有这三种角色的元素上使用aria-disabled
,除非它们处于网格或树网格的上下文中。在使用
aria-valuemax
要保证值大于等于aria-valuemin
的值;反过来aria-valuemin
的值不能大于aria-valuemax
的值。如果当前值是未知的(例如一个不确定的进度条),则不应该设置
aria-valuenow
属性。如果aria-valuenow
属性不存在,则不暗示有关当前值的任何信息。
Live Region Attributes(LRA)
包含特定于富Internet应用程序中实时区域的属性。这些属性可以应用于任何元素。这些属性的目的是指示内容更改可能会在没有元素具有焦点的情况下发生,并为辅助技术提供有关如何处理这些内容更新的信息。
属性 | 值类型 | 描述 |
---|---|---|
aria-atomic | true /false | 根据aria相关属性定义的变更通知,指示辅助技术是显示全部变更区域(false ),还是只显示部分变更区域(true )。 |
aria-busy | true /false | 表示元素是(true )否(false )正在被修改,辅助技术可能希望等到修改完成后再将其暴露给用户。 |
aria-live | token:assertive : 指示对该区域的更新具有最高优先级,并应立即呈现给用户。off :指示除非用户当前关注该区域,否则不应向用户显示对该区域的更新。polite :指示对该区域的更新应在下一个合适的时机显示,例如在当前句子结束时或用户暂停键入时。 | 指示将更新元素,并描述用户代理、辅助技术和用户可以从活动区域期望的更新类型。 |
aria-relevant | token list:additions :元素节点被添加到活动区域内的可访问性树中。additions text :相当于值的组合。all :相当于所有值的组合。removals :活动区域内的文本内容、文本替代或元素节点将从可访问性树中删除。text :文本内容或文本替代将添加到活动区域的可访问性树中的任何后代。 | 指示当活动区域内的可访问性树被修改时,用户代理将触发哪些通知。 |
注意:这部分状态/属性也均属于GSAP。适用于所有角色,并且没有继承到其他角色当中。
Relationship Attributes(RA)
包含指示元素之间的关系或关联的属性,这些关系或关联无法从文档结构中轻松确定。
属性 | 值类型 | 描述 | 角色使用 | 继承到角色 |
---|---|---|---|---|
aria-activedescendant | ID reference | 当DOM焦点在组合widget 、combobox 、textbox 、group 或application 上时,标识当前处于活动状态的元素。 | application 、combobox 、composite 、group 和textbox | grid 、listbox 、menu 、menubar 、radiogroup 、row 、searchbox 、select 、spinbutton 、tablist 、toolbar 、tree 和treegrid |
aria-colcount | integer | 定义table 、grid 或treegrid 中的列总数。 | table | grid 和treegrid |
aria-colindex | integer | 定义元素的列索引或相对于table 、grid 或treegrid 中的列总数的位置。 | cell 和row | columnheader 、gridcell 和rowheader |
aria-colspan | integer | 定义table 、grid 或treegrid 中的单元格或grid 单元跨越的列数。 | cell | columnheader 和rowheader |
aria-posinset | integer | 标识在一个具有相同角色的一组元素中,当前元素在该组元素中的位置。 | article 、listitem 、menuitem 、option 、radio 、row 和tab | menuitemcheckbox 、menuitemradio 和treeitem |
aria-rowcount | integer | 定义table 、grid 或treegrid 中的行总数。 | table | grid 和treegrid |
aria-rowindex | integer | 相对于table 、grid 或treegrid 中的行总数定义元素的行索引或位置。 | cell 和row | columnheader 、gridcell 和rowheader |
aria-rowspan | integer | 定义table 、grid 或treegrid 中的单元格或网格单元格所跨越的行数。 | cell | columnheader 和rowheader |
aria-setsize | integer | 定义当前列表项或树项集合中的项数。 | article 、listitem 、menuitem 、option 、radio 、row 和tab | menuitemcheckbox 、menuitemradio 和treeitem |
WAI-ARIA 1.2规范:
当在具有DOM焦点的元素上设置
aria-activedescendant
的值时,作者必须确保满足以下两组条件之一:
aria-activedescendant
的值指向一个已存在的元素。已存在的元素可以是具有DOM焦点的元素的后代,也可以是由aria-owns
属性指示的逻辑后代。- 具有DOM焦点的元素是组合框(combobox)、文本框或搜索框,其中
aria-controls
引用支持aria-activedescendant
的元素,并且aria-activedescendant
的值指向被控制元素的所拥有的元素。对于
aria-colindex
:作者必须将aria-colindex
的值设置为大于或等于1
、大于同一行中任何先前元素的aria-colindex
值以及小于或等于整个表中的列数的整数。对于跨多个列的单元格或网格单元格,作者必须将aria-colindex
的值设置为跨度的开头。
重复的状态/属性
属性 | 值类型 | 描述 | 角色使用 | 继承到角色 | 所属分类 |
---|---|---|---|---|---|
aria-controls | ID reference list | 标识当前元素控制着哪些元素的内容或存在。 | - | - | GSAP和RA |
aria-describedby | ID reference list | 指定一个或多个元素ID,这些元素包含有关当前元素的其他说明或描述。 | - | - | GSAP和RA |
aria-details | ID reference | 标识为对象提供详细的扩展描述的元素。 | - | - | GSAP和RA |
aria-flowto | ID reference list | 标识内容的替代读取顺序中的下一个(或多个)元素,根据用户的判断,允许辅助技术覆盖按文档源顺序读取的一般默认值。 | - | - | GSAP和RA |
aria-hidden (状态) | true /false /undefined | 指示元素是(false )否(true )公开给无障碍API。或者由用户代理觉得是否隐藏(undefined )。 | - | - | GSAP和WA |
aria-errormessage | ID reference | 标识为对象提供错误消息的元素。 | application 、checkbox 、combobox 、gridcell 、listbox 、radiogroup 、slider 、spinbutton 、textbox 和tree | columnheader 、rowheader 、searchbox 、switch 和treegrid | WA和RA |
aria-labelledby | ID reference list | 将一个或多个元素的标签文字关联到当前元素 | 基本标记的所有元素,但这些角色除外:caption 、code 、deletion 、emphasis 、generic 、insertion 、paragraph 、presentation 、strong 、subscript 和superscript | - | GSAP(除非另有禁止)和RA |
aria-owns | ID reference list | 标识一个(或多个)元素,以便在DOM元素之间定义视觉的、功能的或上下文的父/子关系(DOM层次结构不能用来表示这种关系)。 | - | - | GSAP和RA |
注意:
- WAI-ARIA 1.2规范:建议作者在对辅助技术隐藏可见的呈现内容时要非常谨慎,并考虑到各种残疾。
- 对于
aria-errormessage
:使用时必须确保内容不被隐藏,以便用户可以导航到并检查错误消息。
角色和状态/属性的关系
一个角色可以有多个状态/属性提供特定信息,一个状态/属性也可以适用于多个角色。以角色为主,与状态/属性会有以下几种关系:
- 必需:是角色和子类角色专门需要的状态/属性,作者必须为所需的状态/属性提供非空值。并且不能对所需状态和属性使用
undefined
值,除非undefined
是该undefined
状态或属性的显式支持值。 - 支持:特别适用于角色和子角色的状态/属性。可以为支持的状态和属性提供值,但在默认值足够的情况下不需要。
- 继承:角色从超类角色继承的属性的信息性列表。状态/属性继承自角色模型中的超类角色,而不是从DOM树中的祖先元素继承。
- 禁止:角色上禁止的状态/属性的列表,不得指定禁止的状态或属性。
注意:不是所有角色都会与状态/属性存在这四种关系,有些角色可能没有必需状态/属性。
根据规范的内容,角色与其必需状态/属性如下:
更多细节请查看使用 ARIA:角色、状态和属性。
与WCAG的关系
WCAG和WAI-ARIA都是为改善Web内容的无障碍性而设立的标准和规范。但它们有几点不同:
- 定位不同:WCAG主要关注整个页面的可访问性;而WAI-ARIA则专注于交互式控件的可访问性。
- 要求不同:WCAG包括一系列的指南和成功标准,其中有些是强制性的,有些是建议性的;而WAI-ARIA则提供了一组技术来实现辅助技术的可访问性支持。
- 使用方法不同:WCAG是通过静态页面内容和结构来改善可访问性;而WAI-ARIA是通过添加语义信息来改善交互式应用程序的可访问性。
另外WAI-ARIA补充了WCAG的内容,它们可以一起使用来实现更好的无障碍性。可以在元素上使用WAI-ARIA来增加交互式组件的无障碍性,并遵循WCAG的标准来确保页面的整体可访问性。
CSS与JavaScript无障碍
目前在一些设计社区中有很多大佬写了各种设计指南,对各种细节,包括字体样式、行高、滚动条、颜色对比度等等很多方面都有详细的建议和说明,大家也看得不少了,我就不过多赘述了。可以参考:
。。。
虽然这些指南不是标准规范,但是也能很大程度地提高Web应用的无障碍访问和应用美感。要注意,这些指南适用于各种Web应用,不只是CSS方面,只是对于我们前端开发人员来说,最主要直接方便能对这些指南细节进行设置和调整的莫过于CSS了。
而在JavaScript中,支持无障碍的最佳实践的一种是要遵循unobtrusive JavaScript原则,这是一种编写JavaScript代码的方法,主要原则是将JavaScript代码与HTML文档分离,使得网页的基本功能能够在没有启用JavaScript的情况下正常运行,并且在启用JavaScript时能增强性能。具体请查阅Unobtrusive JavaScript 不唐突的JavaScript的七条准则。
另外,还可以利用Javascript内置的API来实现各种无障碍功能,这点下文有说明。除此之外,还有其他JavaScript无障碍问题,比如尽可能让所有交互元素都可以用键盘进行访问以执行某些复杂的功能,而不仅限于鼠标等等。
实践
以在安卓和Windows环境下使用屏幕阅读器为例,先看一个简单的🌰:
<div
tabindex="0"
role="progressbar"
aria-valuemin="0"
aria-valuemax="100"
aria-valuenow="50">50</div>
这是一个经典的用<div>
模仿进度条的🌰,该元素的当前值为50
,且设置了tabindex
保证元素能通过键盘获得焦点。在Windows中按Win+Ctrl+Enter打开讲述人,打开之后再按Tab聚焦到该<div>
上,会读出50%,进度指示器
;在安卓手机上在设置里的辅助功能或者直接呼出对应的智慧助手,喊一声打开屏幕朗读,确定后再切回网页中,手指点击该元素时也会读出50%,进度指示器
。
再来看一个复杂点的🌰:
<template id="timer-template">
<div id="time" tabindex="0" role="timer"></div>
</template>
<script>
class Timer extends HTMLElement {
connectedCallback() {
this.attachShadow({ mode: 'open' });
const template = document.getElementById('timer-template').content;
this.shadowRoot.appendChild(template.cloneNode(true));
this.timeElement = this.shadowRoot.querySelector('#time');
this.timeElement.textContent = this.getCurTime()
setInterval(() => {
this.timeElement.textContent = this.getCurTime()
}, 1000);
document.querySelector('my-timer').addEventListener('focus', () => {
/* 由于自动设置aria-label值会导致屏幕阅读器重复读出aria-label的值,
因此这里采用获得焦点时才设置aria-label值,
实际屏幕阅读器也只会读出元素获得焦点时的时间,而不是最新时间
*/
this.timeElement.setAttribute('aria-label', this.getCurTime());
})
}
getCurTime() {
const now = new Date().toLocaleString();
return `当前时间为:${now}`
}
}
customElements.define('my-timer', Timer);
</script>
<my-timer></my-timer>
这是一个WAI-ARIA和Web Components结合使用的🌰,<my-timer>
是一个包含年月日时分秒且自动更新时间的自定义组件,当在Windows环境下用屏幕阅读器聚焦该元素时会读出当前时间为:xxx,计时器
,安卓也是一样。
注意:
在元素上使用
role
和aria-*
等属性只是为了帮助开发人员提供更好的无障碍信息,并不会改变元素的本质,他们的样式以及在JavaScript中访问元素的方式都是不变的,这些与辅助技术无关。对于一些文本(比如阿拉伯数字),一些屏幕阅读器会以网页的
lang
属性为主,读出相应的语言表示,而不是本地端系统的语言。上面的两个例子和刚开始的国家政务服务平台的读屏功能的实现方式是不一样的,他们是在代码中插入了TTS引擎服务,或者是通过JavaScript提供的音频API,动态设置音频的地址,再播放音频等等方式做的自定义读屏效果。
一些流行的TTS引擎有Google Cloud Text-to-Speech、Microsoft Azure Text-to-Speech、讯飞语音引擎...更多可以自己在网上搜索。
测试
虽然在上面的🌰中屏幕阅读器读出了我们想要的内容,但是不代表着这样就成功了,比如在上面的第一个🌰,用Lighthouse测试时,无障碍部分只有96分,扣的4分在于ARIA progressbar elements do not have accessible names
,表示元素没有可访问名称。需要使用aria-label
/aria-labelledby
属性提供可访问名称。没有设置aria-label
/aria-labelledby
属性,可能会导致使用屏幕阅读器等辅助技术的用户无法理解它的用途。
自动测试工具
Lighthouse
Lighthouse是由谷歌开发的性能分析工具,可用于评估Web应用程序包括无障碍、最佳实践、性能和SEO等方面。使用Lighthouse的一大好处是方便,它内置在大部分主流浏览器的DevTools中。打开浏览器控制台就可以使用:
如图的accessibilit就是无障碍测试的结果,点击即可跳转至无障碍的结果部分查看相关内容。
另外Lighthouse也有浏览器插件版和工具包版。
Axe-core
Axe-core是网站和其他基于HTML的用户界面的无障碍测试引擎。支持多种平台和环境并且可以与测试框架结合使用。
使用方式(以上面第一个🌰为例):
-
安装:
npm install axe-core --save-dev
-
导入:
<script src="node_modules/axe-core/axe.min.js"></script>
-
在
<script>
写入:axe.run((err, results) => { if (err) throw err; console.log(results); });
-
看打印结果,主要看其中的
violations
字段:如图,第零项就是
role='progressbar'
那个元素的测试结果,其中description
字段就是对测试结果的描述,这里它提示我们确保每个ARIA进度条节点都有一个可访问的名称
,跟刚刚Lighthouse的提示类似。另外,在Vs code有Axe-core官方推荐的插件:axe Accessibility Linter,当打开HTML或JavaScript等文件时,插件会自动运行Axe代码分析器来检测是否存在可访问性问题。如果检测到问题,就会在代码下方显示红色波浪线。
。。。
手动测试
如今的网站内容越来越复杂,自动测试很难做到100%检测,比如上面的Axe-core平均可以自动找到57%的WCAG问题。由此可见手动测试的重要性。可以参考设计师如何做无障碍测试进行手动测试。
注意:有些问题并不是WCAG和WAI-ARIA规范的内容,但是我个人认为可能会影响到用户体验。大部分的无障碍测试工具测试无障碍时只会关注无障碍的问题,手动测试也很容易遗漏。对于像兼容性等问题并不在某些工具的测试范围中,比如像上面的第二个🌰我个人觉得存在一定缺陷,因为各浏览器对Web Components的兼容性并不是很好,尤其是移动端,安卓自带的浏览器要112以上版本才兼容Web Components,在安卓浏览器中打开直接一片空白。并不是所有人的浏览器都是全新版本。但是在Lighthouse中测试移动设备的无障碍仍然显示是100分。
所以在测试的时候要考虑全面,设身处地将自己带入到各种人群中,思考他们会怎么使用你写的应用程序。
其他
无障碍功能树
无障碍功能树(Accessibility Tree)是一个虚拟的树形结构,是一种将 Web 内容和应用程序中的无障碍功能组织和描述的方法。反映了Web页面中的无障碍信息,并且可以被辅助技术使用。无障碍功能树通常由浏览器自动生成,与DOM树类似,但DOM树只反映了文档的结构和内容;而无障碍功能树还包含许多其他属性,例如元素的角色、状态、名称和值等。
在浏览器打开开发者工具照着下面的步骤就可显示无障碍功能树了:
通过无障碍功能树可以帮助开发人员更好地了解无障碍功能的层次结构和如何实现这些功能。同时,还可以帮助测试人员和用户更好地理解无障碍功能的组成部分,以便更好地测试和使用无障碍应用程序。
相关JavaScript API
这部分主要介绍两个与屏幕阅读器有点类似的API。但要强调一下与我们平时的方式方法没什么不同,我们可以使用常见的方式方法结合WCAG和WAI-ARIA规范实现无障碍访问,比如一样可以用setAttribute()
方法设置role
和aria-*
属性。只不过学习了Web无障碍了之后,要考虑的东西可能会变得比平时更多。
注意:在使用这些API时要考虑兼容性!
Web Speech API
Web Speech API能够将语音数据合并到Web应用程序中。包含两个组件:SpeechRecognition
(语音识别,即语音→文本)和SpeechSynthesis
(语音合成,即文本→语音)。
Tips:可以通过
SpeechSynthesis
接口来实现类似国家政务服务平台的读屏功能。
使用示例
语音识别
点击语音输入按钮开始语音识别,将结果存到<p>
当中。
<button id="sr">语音输入</button>
<p id="result"></p>
<script>
let flag = 0;
// 创建一个新的SpeechRecognition对象
const recognition = new webkitSpeechRecognition()
// 设置语言为中文
recognition.lang = 'zh-CN';
sr.addEventListener("click", () => {
if (flag == 0) {
sr.innerText = '结束'
// 开始语音识别
recognition.start();
// 监听语音识别结果事件
recognition.onresult = function(e) {
// 获取语音识别结果
result.innerText = e.results[0][0].transcript;
};
// 监听语音识别错误事件
recognition.onerror = function(e) {
console.error(e.error);
};
flag = 1
} else {
recognition.stop()
sr.innerText = '语音输入'
flag = 0
}
})
</script>
语音合成
每次点击朗读按钮,就会读一次Hello World!
。
<button id="speakBtn">朗读</button>
<p id="textToSpeak">Hello World!</textarea>
<script>
// 创建SpeechSynthesis对象
const synth = window.speechSynthesis;
// 创建朗读函数
function speak(text) {
// 创建SpeechSynthesisUtterance实例
const utterance = new SpeechSynthesisUtterance(text);
// 设置语言
utterance.lang = "zh-CN";
// 开始朗读
synth.speak(utterance);
}
// 监听按钮点击事件
speakBtn.addEventListener("click", () => {
speak(textToSpeak.innerText);
});
</script>
注意:该例中的SpeechSynthesisUtterance
也是语音合成组件中的一个接口,表示一个发音请求。在语音识别/合成这两个组件中包含了若干个接口,详细可以查看Web Speech API。
Audio API
Audio API(音频 API)各位读者应该很熟了,它是一组用于处理音频的JavaScript接口。允许开发人员创建和控制音频流,包括音频的生成、处理和播放。
Audio API也能做到类似屏幕阅读器的功能,比如在用户鼠标悬停、键盘聚焦、移动端触摸等等时机创建一个Audio
实例,并将准备好的音频赋值给实例的src
属性,接着播放即可。这种也是某个政府网站的屏幕朗读的实现方式的一部分,不过我忘记是哪个网站了。
。。。
写在最后
作为一名程序员,Web无障碍是一个非常重要的话题,学习Web无障碍也是一个非常有意义的过程。它可以帮助我们建立一个更加包容和平等的互联网。通过实现Web无障碍,我们可以让更多的人都能够享受到互联网带来的便利和乐趣,无论他们是否有不同的障碍。同时,这也是一个让我们更好地理解和尊重不同人群需求的机会。