A11Y 101:编写HTML时要考虑可访问性

发布于 大漠

上一节中和大家一起探讨了为什么要构建可访问性和如何构建可访问性应用。文章中我们聊到了HTML中可访问性中的作用,事实上很多开发者都认为HTML对于构建可访问性是非常的重要,特别是用正确的HTML标签来表达正确的意图,可以提升Web应用的可访问性。这篇文章将会详细介绍如何从HTML方面来最大化地提升Web应用的可访问性。

最近一直在阅读和学习构建可访问性方面的知。我花了一些时间去了解一些这方面的知识,但随着了解的越多,让我越来越觉得不应该只把可访问性当作一个检查清单,而应该当作一个起点。将这些技术合并到自己的工作流程中,即从构建Web应用的最开始,就应该把可访问性融入到应用中

HTML是什么

HTMLHypertext Markup Language的简称,俗称超文本标记语言。是一种用来结构化Web网页及其内容的标记语言。到目前最新的规范是HTML 5.2版本。HTML由一系列的元素(大致有**106**个)组成

这些元素可以用来包围不同部分的内容,使其以某种方式呈现或者工作。一对标签可以是一段文本或一张图片等等:

一系列的HTML标签元素组合在一起就构建是Web应用的骨架(哪怕是现在最流行的JavaScript框架,比如Vue或React构建出来的应用也离不开HTML的身影),在没有任何样式(除了客户自带的基本样式)渲染的情况之下,浏览器的效果将以HTML的源渲染,比如下图所示这样:

HTML和可访问性

在社区中经常可以看到这样的一个讨论话题:HTML语义化的重要性。其意思是指我们应该尽可能地用正确的HTML标签来表达正确的意图。特别是在现代Web的开发当中(尤其是使用JavaScript框架,比如React和Vue等),在构建Web页面或应用的时候,越来越多的同学不再考虑HTML的语义化。在今年的React Conf大会上@BrittanyIRL分享的Accessibility Is A marathon, Not A Sprint话题中也特别的提到这方面:

看到上图,你可能有同感!

或许你可能会想,“用户看到的仅仅是呈现后的效果,他们(甚至是你的老板)并不会太多的关注语义化(他们也有可能不懂啥是语义化)”。这样的前提之下,做为开发者,过多的关注语义化有何意义呢?又能有什么作用呢?或者简单地说,为什么语义化对于一个Web页面或应用如此重要

既然如此,我们就先来搞清楚“语义”的概念,以及为什么它们在Web开发中不可或缺,特别是在一个具备可访问性的应用中,它又起着什么作用。

语义化的标签元素

众所周知,HTML(或者说HTML 5.2,仅版本不同)就是一种标记语言。这也意味着它不仅是纯文本,而且是有意义的标记文本,也就是大家所说的语义化。正因如此,HTML提供了很多标签(元素),每个标签都有其固有的独特语义。正确的使用这些标签元素是非常重要的。

没有语义的标签

为了更好的让大家理解语义化的重要性,我们先来看看HTML中没有语义信息的标签。

在HTML中只有两个标签没有任何语义:<div><span>

divspan是大家最为常用的两个HTML标签元素,事实上这两个标签元素是没有具体的语义化信息,如果说有的话,它们都是提供视觉样式化所需的容器,唯一的区别是:<div>是一个块元素,而<span>是一个内联元素

比如下面这样的一段简单代码:

<div>My hobbies</div>

<div>Dancing</div>

<div>Playing soccer</div>

如果在不加任何样式下,用户在客户端(比如浏览器)中看到的效果可能会是像下面这样的(视觉上的效果都是以浏览器自带的字号、行高、颜色等方式渲染):

上面是视觉用户在客户端上所看到的视觉效果。但对于视力有障碍的用户,他们可能更多的情况是会依赖一些特殊的辅助技术来访问,比如屏幕阅读器。那么屏幕阅读器只会读出这些元素的文本内容(如果有的话):

My hobbies
Dancing
Playing soccer

在这种情况之下,不管是视觉用户还是听觉用户所得到的结果都是相同的,即只获取了文本内容,并没有任何语义上的信息。在使用不具有语义信息的标签元素会有一些问题。

可访问性

虽然现在有很多工具很优秀,他能尽自己最大的努力解析页面的结构来帮助用户理解创建者(构建页面的人)的意图,并给给用户提供快速的导航跳转到页面特定的位置。但是<div><span>这样无语义信息的标签并没有真正给出关于文档结构的任何有用信息。就算是最厉害的A11Y工具仍然不属于人类,不能期望它能解析classid以及其他的HTML标签属性,并识别开发人员为其命名的真正意义。比如:

<div class="article-header-level-2"></div>

构建者可以想表达的是一篇文章的二级标题,但机器人(或工具)并不能识别出来。

可读性

对于全文尽是<div><span>的源码,可读性也较差(虽然用户看不到源码,但对于开发者而言是痛苦的)。开发者面对这样的场景,需要仔细阅读标签元素的classid或一些自定义属性。特别是面对标签结构嵌套的层级多的情况之下,要更好的理解就变得更为痛苦。甚至分不清楚哪是哪。很多时候就严重的依赖IDE的特性,比如不同的缩进,不同的颜色等。

一致性和标准

在开启一个新的项目的时候,必须从头开始。特别是面对多人协作的时候,要遵循团队规范的时候,需要按照一定的约束进行编写代码,那将是件痛苦的事情。如果每个人都有一种标准化的方法来编写HTML文档或者说在编写的时候尽可能的使用具有语义信息的标签,那么事情就会变得更容易一些。

有语义的标签

在HTML中除了<div><span>之外剩下的近 104 个标签都是具有语义信息的标签。对于具有语义化的标签主要分为 内容标签元素和结构标签元素两大类。

内容标签元素

内容标签元素主要用于赋予内容意义,比如标题标签(h1~h6)、段落标签(<p>)或链接标签(<a>)等。

让我们来看一个简单的示例,该示例使用了一些内容标签元素:

<h1>My hobbies</h1>

<h2>Dancing</h2>

<h2>Playing soccer</h2>

上面的示例使用了<h#>标题标签元素,告诉客户端,这些不仅仅是纯文本,而是一个特定级别的标题(具有语义化)。同样的,针对这些具有特定级别的标题标签元素,客户端在渲染这些标签时也有特定的样式,比如字号和行高都要更大,在视觉上也会加粗,而且级别不一样,这些都会有所差异。换句话说,客户端给用户呈现的效果上就很直观的传递了这些附加信息。

使用内容标签元素时对屏幕阅读器这样的辅助工具也非常有利,比如屏幕阅读器,除了可以读出标签元素中的文本内容之外,还会传达额外的信息:

My hobbies: heading level 1
Dancing: heading level 2
Playing soccer: heading level 2

这对于一些用障碍人士(视力不好)带来的感觉将是完全不同的(事实上,他们所提供的信息是相同的)。

在HTML标签元素中,内容标签元素除了<h1>~<h6>之外还有很多其他的标签元素,比如我们常见的<p><b><strong><em><i><ul><ol><dl><img><del>等。

说到这里,我想强调一下。虽然很多人都觉得HTML非常的简单,标签虽多,可用到的并不多,没有太多的必要花时间在HTML标签的学习和理解上;但事实上并非如此,如果要真正的用好HTML,或者真正的用好有语义的标签,并不是容易的事情。可能你看到这样的观点会说,怎么可能?如果你的想法也是这样的话,那么在这里提出一个问题:HTML中的<i><em>表现是斜体,<b><strong>表现是粗体,那么它们之间语义是的区别是什么,具体应该怎么使用?试问一下,你能答出来吗?如果答不出来,建议你花点时间阅读《The i, b, em & strong elements》一文。我想你会彻底地改变以前对HTML的看法。

结构元素

结构元素主要用于对Web页面或应用上使用到的元素进行分组,并将它们划分为不同的区域。特别是在HTML5中,结构元素表现的尤其突出。

在HTML5中可以用更具语义信息的方式来标记文档的结构,这种方式可以清楚的告诉开发者它们在文档中的用途。更重要的是,它们是标准化的,每个开发者都可以使用和理解,甚至包括机器人

比如下图:

在还没有HTML5的时候(也就是还没有具体的结构化标签元素时),我们对于一个页面布局一般都是采用div带具有语义的类名或id名来构建(主要便于开发者阅读源码,别无他用),有了HTML5之后,更多时候采用<header><main><nav><article><footer>等结构化标签来替代以往的使用方式。

虽然这些结构性标签具有一定的语义信息,但在使用的时候还是有很多细节,比如下图所示:

有关于这方面的更多的讨论可以阅读下面所列的文章:

合理的使用结构化标签元素之外还有一个好处,那就是这些标签元素能为屏幕阅读器(或其他辅助技术工具)提供额外的语义,为用户提供有关他们正在浏览的内容的额外信息。

话又说回来,HTML中有近106个标签元素,除了divspan标签无任何语义信息之外,其他的标签都具有一定的语义含义。实际生产中,面对这么多的标签,如果无法准确的判断在什么时候应该使用什么标签更合的话,可以借助下图来帮助你做出更合理的选择:

或者花一些时间真正的去了解每个标签的含义:

语义化元素有助于可用性

使用语义化元素特别是一些UI控件(指的是与用户交互控件),比如buttona和表单控件是具有高可用性。浏览器都知道如何处理它们,它们为用户提供了与网站进行适当交互所需的一切。比如<select>下拉选择框就是一个很好的例子。如果在Web中使用默认的<select>表单控件要比使用<div>模拟下拉选择框的可用性更高。如下图所示:

上图中左侧是Safari浏览器下的表现效果,右侧是iOS移动设备上的表现效果

看到上图,你应该能较好的体会到使用原生的有语义的标签其实是很容易的事情,但却作用是极大的,能较好的满足各种终端,在交互上也更合理,也将帮助开发者节省更多的开发时间。另外,让浏览器来完成大部分工作也更符合未来发展的需要。未来将会发布更多具有不同预期交互的设备终端。当这种情况发生时,设备的浏览器可以根据这些交互来调整我们的站点。然后,我们可以花时间做一些比每个新设备重写代码更有趣的事。

不过在Web开发中独立具备可交互的UI控件的语义化标签元素是有限的。换句话说,在使用divspan来模拟一些具有轻量的交互控件时,是可以借助CSS和JavaScript来实现,只不过较为痛苦些,费时些,特别是面对众多不同终端要提供不同交互方式时。另外,如果要让自定义UI控件更具可访问性的话,除了使用HTML、CSS和JavaScript还是不够的,还需要借助WAI-ARIA方面的能力。如果你对这方面感兴趣的话,可以阅读:

使用语义化时的典型问题

很多开发者在使用语义化的过程中或多或少会出现一些问题。比如无意义的语义、错误的语义,甚至是语法上的错误。

无意义的语义

在开发Web页面或Web应用的时候,从设计师到开发者都喜欢在视觉上用颜色对某些UI控件做区分。虽然这样做在客户端上显示是没有任何问题,但对于视力有障碍的用户群体而言,这是致命的。比如说,色盲的用户无法较好的区分颜色,如下图所示:

上图左侧是正常用户看到的效果,右侧是有色盲的用户看到的效果

而且一些辅助技术工具,比如屏幕阅读器是不会关心视觉上的表现结果。不管文本的颜色是blue还是red,字号是1px还是100px。换句话说,对于屏幕阅读器来说,它只关心标签,特别是语义化的标签对于屏幕阅读器是非常有利的。

有关于语义化标签和屏幕阅读器之间的关系是较为复杂的,在这里我们不做过多的阐述,在后续的文章中我们会单独聊这方面的事情。如果你急着想了解它们之间的故事,可以先阅读《Semantics to Screen Readers》一文,或者单击这里了解更多有关于这方面的内容

所以说,如果用不同的视觉效果来描述语义或者呈现不同之处,那是毫无意义的。这样做对于部分有障碍的用户群体是无意义的语义。正如《创建可访问性网站并不是想得那么难》文章中所提到的一个重点:

别仅依赖颜色作为关键信息传递!

错误的语义

另外一个典型问题是语义错误

就拿开发人员在构建Web页面模板为例。对于具备一个良好的结构(HTML模板结构)的Web页面或应用程序除了在可读性有利之外,对于SEO以及我们今天聊的可访问性都有利。在社区中一直都有人在提倡CSS裸奔日(CSS Naked Day)

CSS Naked Day是指简单地从你的网站上删除所有CSS,完全去除它的样式设计,用来检测最佳的Web开发实践

其实这个方式也可以用来检测我们在结构语义化上使用是否正确。

就拿一个Web页面中的标题(<h#>)的使用吧,有人用得恰到好处,有人却用得渣渣(并不是用不好,而不是不用心去考虑),比如下图所示:

没有对比就没有伤害!

就上图示例而言,前者虽然在视觉上看起来像标题,但屏幕阅读器并无法正确地和标题的级别结合在一起(前面介绍内容标签是有介绍过)。

无效的语法

虽然HTML是种标记语言,就使用规则上来说,它也有非常严格的规则。但由于它是一种标记语言,它们比其他程序语言更容易被计算机理解。而且现代浏览器对其错误的使用容忍度越来越高,很多时候会自动修复HTML代码中一些典型的语法问题。就这方面而言,对于一些粗心大意的开发者来说是件好事,但这对于语义上来说是不友好的。

HTML是一个非常严格的标准(特别是DOCTYPE使用严格模式),必须正确编码才能真正有效。由于无效的语法通常会导致语义的缺失或不正确,而且屏幕阅读器更依赖于有效的HTML代码。

在现代Web开发当中,特别是在基于一些JavaScript框架做开发的时候,有时候开发者会忽略一些细节,造成HTML结构的无效(无效的语法)。比如React中<React.Fragment>的使用用例:

熟悉React的同学一眼就能看出来,上图左侧编译出来的HTML结构:

<table>
    <tr>
        <div>
            <td>Hello</td>
            <td>World</td>
        </div>
    </tr>
</table>

从HTML结构上来说,这样的结构并不是好的结果,甚至是无效的结果(根据DOCTYPE的声明有关)

如果你不知道自己有没有因为打盹写了个无效的HTML,那么可以使用W3C Markup Validation Service来验证HTML:

社区中很多开发者都认为构建语义化的HTML是非常有必要的,特别是对于Web的可访问性来说更是如此。如果你在项目的一开始就使用带有语义化的HTML标签,不仅不会花更多的时间,而且又有以下的可访问性优点:

  • 更便于开发:正如前面提到的,使用具有语义化的HTML更易于理解,并且可以毫不费力的获得一些功能
  • 更适配移动端:语义化的HTML文件比非语义化的HTML文件更加轻便,并且更易于响应式开发
  • 更便于SEO优化:比起使用非语义化的<div>标签,搜索引擎更加重视在“标题、链接”等里面的关键字,使用语义化可使网页更容易被用户搜索到

HTML对可访问性的影响

前面我们花了一点时间了解了HTML和可访问性的关系,尤其是具有语义化的HTML标签元素对于Web可访问性更有利。接下来,我们再花一些时间来进一步了解HTML对可访问性的影响。

对屏幕阅读器的影响

在《A11Y 101:构建可访问性应用的2W1H》一文中,我们知道有些用户群体是依靠一些辅助技术(比如屏幕阅读器)来访问Web页面或Web应用。

语义化HTML可以为屏幕阅读器提供更好的上下文,能更好的将屏幕上的内容读给用户听

所有Web页面或应用都有一个堆栈的结构,即可HTML结构是有一个顺序,其顺序就是按照其源码出现的时间来排列的。在没有额外的布局处理,呈现给用户的视觉效果是按源码顺序从上往下排列(从左往右排列)。如果给Web页面添加了CSS样式之后,呈现的视觉效果可以根据设计风格来定。对于视力正常的用户群体浏览带有视觉效果Web页面或应用没有任何问题,而且可以以非线性的方式导航到想要浏览的内容。但对于屏幕阅读器输出音频(有时是盲文)时,这些视觉提示不能以同样的方式使用。换句话说,屏幕阅读器无法按带有视觉效果的方式(或非线性)方式输出音频。

屏幕阅读器提供了另一种导航方式,使人们能够在不同类型的内容之间跳转,比如链接、表单、标题、列表和段落。如果我们所有内容都是使用无任何语义化的标签,比如divspan,就不能给屏幕阅读者有一个较好的方式来索引有价值的内容。

对键盘导航的影响

对于视力有障碍的用户喜欢用屏幕阅读器这样的辅助技术来访问一个Web应用,但对于行动有障碍的用户(或高度依赖于键盘的用户)更喜欢通过键盘来访问一个Web应用。在Web中有很多元素,比如表单控件导航菜单视频音频对于依赖键盘访问的用户来说尤其困难。例如,我们在Web应用中(特别是在PC端)会使用鼠标悬浮在具有下拉菜单上,并同时移动鼠标来选择所需要的菜单项,比如下面这样的效果:

这样的交互效果对于依赖于键盘操作的用户群体来说可能是非常复杂或者非常困难的。

值得幸运的是,我们同样可以使用具有语义化的HTML将大部分交互留给浏览。比如语义化表单控件可以表示是否选中了某个复选框,或者哪个标签(<label>)与哪个输入字段(<input>)相关联。这些默认的行为可以决定用户是能够继续使用表单还是出于无奈离开。

在Web构建中,我们是可以通过一些细节将这方面做得更好。换句话说,我们在构建一个Web页面或应用程序时,编写HTML应该注意些什么,或者怎样构建一个HTML对于应用的可访问性更好。接下来,我们一起来讨论这方面的细节和需要注意的事项。

如何构建适合可访问性的HTML

在开始之前,我需要再次强调一下。虽然随意使用任何HTML标签都可以构建出一个Web页面或应用,但是要构建一个具有可读性,可访问性的应用,编写HTML还是不容易的。我们在编写HTML时,可以参考下面提到这些点可以更容易帮助我们构建出一个具有语义化的HTML,对代码更具可读性,对应用更具可访问性的应用。

使用最具语义化的HTML标签元素

当你使用到divspan这样无任何语义化标签元素时,首先要去思考或者检查是否有更好的HTML标签元素来完成这项工作。结合交互的行为和标签元素的作用来考虑怎么去选择最合适的HTML标签元素。

比如说,使用带有类名的div(比如.nav.header.main)时是最合适的吗?在HTML中有没有带有这方面语义化的标签元素(在HTML5中已有这方面语义化的标签元素,比如<nav><header><main>)。使用特定的元素除了让HTML更具语义化之外,对于代码的维护和阅读也是有利的,而且使用这些特定的元素还可以简化CSS的编写,以最小的工作量确保设计的一致性。

而且前面也不断的提到过,使用具有语义化信息的HTML标签元素对于屏幕阅读器也是有益处的。比如视觉上效果看上去是**“标题”**的,使用<h#>标签元素比其他任何元素都更合适。因为屏幕阅读器除了会读出标签的文本内容之外,还会告诉用户标题的等级,但使用非<h#>的元素,屏幕阅读器只会读出元素的文本内容。

或者说编写HTML时应该像@Estelle Weyl所说的那样

用一句话来概括的话:

在编写HTML的时候,对于无任何语义化的<div><span>标签应该被最后使用。即找不到任何有语义化的标签时才使用这两个标签

结构和样式的分离

编写HTML的时候不要基于CSS中的样式(UI视觉风格)来做选择。现在开发Web应用的时候,对于CSS的选择器通常的做法是使用类名而不是元素。尽管你希望所有文本都是大而粗的,也不太可能将页面中所有内容都放在<h1>元素中。也就是说,我们应该专注于没有样式来编写Web应用所需要的HTML结构,抛开视觉上的诱惑更有助于帮助我们选择最具语义化的HTML标签元素。换句话说:

样式(UI视觉)和内容(HTML)分离,使用有语义的代码来传达含义

我们来看一个简单的示例,在移动端开发的时候,很多开发者(包括我自己)会将一个带有.btn类名的div来做按钮,虽然借助于CSS的特性让这个div看起来是一个按钮,但我们都知识,它只是看上去是按钮,只有<button>才是按钮,才会表现的像是按钮。

@Vadim在今年年初的时候,在SmashingMagazine上就抛出过一个类似的话题(When Is A Button Not A Button?

针对同一话题社区中讨论也是非常激烈的:

在构建Web应用的时候类似的案例还有很多,在这里特意拿这个案例主要是想告诉大家:

在编写HTML的时候不要被UI视觉迷惑,应该根据内容、功能来选择最合适的HTML标签元素

定义文档的语言类型非常重要

在今年双11的互动项目中处理可访问性就踩过这样的一个坑。我们的应用是中文版,但无意之中在<html>标签元素上显式的设置了lang="en",结果屏幕阅读器在读屏幕的时候,是英文(有的时候甚至听不懂),和我们所要提供的效果差十万八千里。后来将lang重置为zh-CN之后,屏幕阅读器恢复正常。

从这个案例来说,显式告诉浏览器在文档中使用哪种语言是好处的。它可以帮助第三方翻译工具和浏览器识别正确的语言和字典,甚至对SEO的优化也是有益处的。在HTML页面中定义正确的语言还有助于辅助技术(比如屏幕阅读器)选择正确的语音配置文件或字符集。

除了在<html>中设置语言类型之外,如果你想在文档中切换语言,可以在特定的标记上使用lang属性,比如:

<html lang="zh-CN">
    <body>
        <p>我是大漠,我想学习<i lang="en">English</i></p>
    </body>
</html>

因此,大家在编写HTML的结构的时候,一定要定义正确的lang。如果不正确使用lang属性会让屏幕阅读器选择不匹配的语音配置文件或字符集(我在实战项目中就踩过这样的坑)。也就是说,为了确保整个页面的正确发音,必须为<html>元素指定有效的BCP 47语言

如果你使用Lighthouse对Web可访问性做检测的话,如果<html>中没有设置有效的lang属性时,检测是无法通过的:

有关于这方面更多的介绍还可以阅读下面相关文章:

隐藏内容

在我们构建Web页面或应用的时候,总是会碰到隐藏内容的情景:

  • 隐藏纯文本内容
  • 用图片替换内容

如果你阅读了《图片替换文本CSS方法》一文的话,你至少想到了十多种隐藏Web元素的姿势:

  • 设置display的值为none,这种方法也被称为FIR方法
  • margin值(一个足够大让元素移出视窗外的负margin值)
  • text-indent值(text-indent具有一个足够大的负值时,可以达到隐藏文本的效果)
  • 设置heightfont-sizeline-height值为0,让元素在视觉上不可见
  • CSS的clip让元素在视觉上不可见
  • position:absolute配合任何一个足够大的leftrightbottomtop的负值,让元素不在视窗范围内显示
  • visibility设置值为hidden在视觉上让元素不可见
  • opacity设置值为0在视觉上让元素不可见
  • clip-path让元素在视觉上不可见
  • HTML元素添加hidden属性,让元素不可见
  • filteropacity()取值为0在视觉上让元素不可见
  • mix-blend-mode在高亮模式下,让视觉上元素不可见

你应该还能想到有类似的其他方式,让一个Web元素真的或假的(视觉上)隐藏起来。

这些隐藏内容的方法对于可访问性也是有一定的影响,特别是对于屏幕阅读器,在《Web隐藏术》一文中深入的探讨了一些有关于Web隐藏术相关的内容,这里不做过多的阐述,用下面的表格来描述他们之间的差异以及对可访问性的影响:

隐藏术 是否有盒模型 是否影响布局 屏幕上是否可见 屏幕阅读器是否可读 元素是否可操作
display: none
hidden="true"
visibility:hidden
opacity:0
position:absolute
clip-path
mask
改变盒型尺寸
transform

上面表格中所列的是我们常见的隐藏内容的方式,既有HTML方面的,也有CSS方面的,但大多数是CSS方面的。如果抛开CSS不说,上面方案中属性HTML方案的也就是通过元素属性hidden。在HTML标签元素上显式设置hidden的值为**true**,那么该元素的内容不管是在视觉上或者是一些辅助技术(如屏幕阅读器)都会被隐藏。

如果在HTML中的标签元素显式的设置了hidden或者hidden="true"属性,浏览器会将其对应的样式设置为:

[hidden]{
    display: none;
}

这是属性客户端自带的样式(User Agent Styles),而不是开发者自己为Web应用中元素构建的样式。在WAI-ARIA中有一个属性aria-hidden类似于HTML的hidden,不同的是 hidden不管是视觉上还是一些辅助技术工具(屏幕阅读器)都会被隐藏,但aria-hidden只对屏幕阅读器生效(如果没有添加额外的CSS情况之下)

aria-hidden还有一个不同的特性。虽然aria-hidden可以让屏幕阅读器和其他辅助技术隐藏该元素及其所有子元素,实际上是将元素及其所有子项从无障碍树中移除(只有 aria-labelledbyaria-describedby 属性引用的元素例外)。但如果该元素是一个可聚集元素,即使aria-hidden设置的值为true,对于屏幕阅读器会被隐藏,但对于键盘用户来说,仍然是可以导航到的。即当键盘用户使用Tab键导航到显式设置aria-hidden="true"的可聚焦元素时,该元素是可聚焦的,只不过焦点可用性在不同的浏览器中有所不同,但一般来说,对于<a><area><audio controls><button><iframe><input><select><summary><textarea><video controls>、具有contentEditable属性的任何元素和将tabindex属性设置为-1的任何元素。

借这个机会跟大家说一个有趣的事吧。早在2016年@Catalin Rosu针对HTML和SVG中有趣的用法做过一个简单的统计,其中就有隐藏元素这一项,下面就是一个统计数据:

如果你想深入的了解[hidden]aria-hidden相关的知识,可以花一些时间阅读下面这些文章:

不要遗忘<img>alt属性

在Web页面或应用中,图片很多时候是重要的内容。俗话说:“一图胜过千言万语”。

如果使用<img>作为内容时,应该给<img>添加alt属性,用来简单地描述图像的内容和功能。如果你通过alt给图像描述内容或功能的时候,最好不要以"Picture of""Image of ""Graphic of "或"图像"这样的关键词,因为屏幕阅读器在读屏的时候,如果读到<img>时就会告诉用户这是一张图片。

如果图像纯粹是用来装饰页面的,或者没有添加有价值的信息,可以考虑用CSS的背景图片。即使你使用了<img>标签,那么也不要删除alt,可以让alt的值为空。

在HTML中使用<img>时,给其显式设置alt属性的值有三个好处:

  • 可以很好的描述图片的信息,对于屏幕阅读器的用户来说,可以了解到图像对应的信息
  • Web页面或应用在加载<img>失败时,在Web上会显式alt属性中的内容,利于可读性
  • alt属性为搜索引擎爬虫提供了更好的图像上下文信息,可以帮助它们正确的索引图像

比如:

<img src="pupdanceparty.gif" alt="Puppies dancing">

我们在给<img>添加alt时,应该怎么添加呢?可以按照下面这些原则来进行:

  • 尽可能具体地描述图像alt首先是为那些无法看到图像的用户提供文本解释。如果一个图像真的没有传达任何意义或价值,只是为了设计而存在,那么它应该存在于CSS中(背景图像),而不是HTML中
  • 保持简短alt中的内容对于屏幕阅读器来说是友好的,但并不是说任意字符串都是友好的。最受屏幕阅读器欢迎的alt的文本长度应该限制在**125**个字符左右,因此建议将其限制在该字符数以内
  • 使用关键字:在alt中尽可能使用关键字来描述,这样除了可以描述图像信息之外,还可以在页面中提供关键字的另一个机会。如果从搜索引擎的角度来说,多给了搜索引擎发出有用号,表面你的页面与特定的搜索查询更匹配,更利于被搜索引擎搜索到。虽然alt中的文本首要任务是用来描述图像和为图像提供上下文信息,但是这样做是有意义的,即在页面上至少一个图像的alt文本中包含你的关键字
  • 避免关键字堆砌:虽然关键词对于搜索引擎是有用的(可以发送更有用的信号),但也不能将很多个关键字都往alt中堆。这样做,像Google这样的搜索引擎虽不会扣分,但这样做反而是画蛇添足。因为alt的重点(目的)是用来描述图像的(试想一下,在alt中添加了很多和图像不匹配的关键字,对于依赖于辅助技术的用户而言,反而会令他们更无法理解图像的信息)
  • 不要在alt文本中包含“Image of”、"Picture of"、"Graphic of "和“图像”等alt<img>标签元素的属性,实际上已经指向一个图像,所以没必要指定它
  • 不要忘记longdes属性alt对于屏幕阅读器最佳的字符数是125个字符,但很多时候你希望用更详细的文本向用户描述图像,那么这个时候应该使用longdes这个属性
  • alt属性可以是空值,但<img>不能没有该属性:所有图像都应该有一个alt标签,但可以是空值(即空字符串),当图像不想表达任何的信息或上下文时(比如一些用于设计元素的图像),那么可以让alt属性的值为空。这样做可以让屏幕阅读器跳过该图像。注意:使用空值的alt和不使用alt是有本质区别的

来看一个简单的示例:

在使用img引入这张图像的时候,下面这些方式都是正确的,但有好的,也有不好的(主要是指alt的使用):

<!-- 不好的用法: 没有使用alt -->
<img src="bird.png" />

<!-- 一般般的用法:显式设置了`alt`,但是个空值 -->
<img src="bird.png" alt="" />

<!-- 好的用法:alt描述了图片的信息(公鸡),告诉用户这是只公鸡 -->
<img src="bird.png" alt="公鸡" />

<!-- 更好的用法:alt描述了图片的信息(公鸡啼鸣),告诉用户这是只正在啼鸣的公鸡 -->
<img src="bird.png" alt="公鸡啼鸣" />

<!-- 最好的用法:alt描述了图片的信息(红色冠公鸡啼叫),告诉用户这是只正在啼鸣的红色冠公鸡 -->
<img src="bird.png" alt="红色冠公鸡啼鸣" />

其实, 如果上面这张图放到不同内容的文章中,那么图片在上下文中所起的使用就不一样了。也就是说,alt的使用时间和内容也取决于上下文,而不仅仅是图像的类型。简单地说,在使用alt的时候,可以像下面这样问自己:

上图来自于@botsofcode的《Alternative Text and Images》一文。

<img>元素除了alt属性之外,还有其他属性,比如title属性,虽然都是文本描述性,但它们之间还是有很大差异的,特别是对于屏幕阅读器。对于这两者之间的差异,在这里暂时不阐述,如果你感兴趣的话,请持续关注后续有关于A11Y相关的更新。

随着技术不断的更新,特别是现在AI智能方面的技术越来越厉害。如果你在面对<img>不知道在alt另如何添加描述内容时,可以把这方面的事情交给人工智能来生成。如果你对这方面知识感兴趣的话,可以阅读@Nino Ross Rodriguez的《Using Artificial Intelligence to Generate Alt Text on Images》一文。

有关于imgalt或者title更多的介绍,可以花点时间阅读下面这几篇文章:

包含描述性内容链接

有些辅助技术工具(比如屏幕阅读器)有时会跳过内容并扫描链接。在这种情况之下,像“了解更多”、“点击这里”这样的短语对于使用屏幕阅读器的用户来说并不友好,也就是可访问性差。为什么呢?因为这样的短语没有提供任何上下文信息或有关于链接的信息。

所以我们在构建HTML时,碰到带有链接的<a>元素时,我们应该尽可能的避免使用短语内容(比如上面提到的“点击这里”)。为了让屏幕阅读器能给用户传达更详细,更准确的信息,在<a>链接中应该包含更具描述性文本信息,比如:

<!-- bad -->
<p>To see our documentation <a href="/README.md">click here</a>.</p>

<p><a href="/full-article">Read more</a>.</p>

<p>
    <a href="accessibility.htm">
        <img src="read-more.jpg" alt="" />
    </a>
</p>

<!-- better -->
<p>We have made our <a href="/README.md">documentation</a> available.</p>
<p><a href="/full-article">Read more - Accessible Landmarks</a></p>
<p>
    <a href="accessibility.htm">
        <img src="read-more.jpg" alt="Click here to read more about accessibility" />
    </a>
</p>

可能在很多时候,不便于在<a>标签内添加可以描述链接的文本信息。在这样不可能做的地方,可以使用aria-label属性来加描述链接的上下文信息,用来帮助屏幕阅读器用户更好的理解。比如:

<a href="post.php?post=632" aria-label="More on Using Meaningful Link Text">More...</a>

有关于这方面的讨论,感兴趣的话还可以阅读:

使用通俗易懂的语言

你使用的语言也会影响可访问性。一般来说,你应该使用不太复杂的清晰语言,不要使用不必要的行话或俚语。这不仅有利于有认知或其他残疾的人;它有利于那些没有用母语写作的读者,年轻人...事实上是每个人!除此之外,你应该尽量避免使用没有被屏幕阅读器清楚读出的语言和字符。例如:

  • 如果可以避免的话,不要用破折号。写 57 ,来替代 5-7
  • 展开缩写 — 写 January,来替代 Jan
  • 展开首字母缩略词,至少一次或两次。 例如写明 “超文本标记语言”( Hypertext Markup Language),而不是直接用缩写 HTML。

允许仅通过键盘访问内容

正如很多视障用户很多时候依赖于屏幕阅读器这样的辅助技术来访问Web页面或应用,同样的很多行动有障碍的用户(或高度依赖键盘的用户)喜欢通过键盘来访问Web页面或应用。这些用户依靠Tab键和键盘上的方向键来浏览。因此我们在构建Web应用的时候需要:

  • 确保所有内容都可以通过键盘来访问Web页面或应用
  • 尽可能提供键盘快捷键
  • 避免使用只在鼠标悬浮时激活的元素

在HTML中所有标签中有些标签元素天生就具备焦点特性,这些元素也被称为可聚焦元素。对于这些元素我们可以很轻易的使用Tab键让这些元素得到焦点。不过也有很多元素是不可聚焦元素,Web浏览器无法检测到这些元素该单击还是悬停。不过幸运的是,HTML可以给所有元素设置tabindex属性,该属性是一个全局属性

tabindex将页面上的顺序或层次结构设置为可选择项。这意味着所有内容都是同等重要的,对于行,从左到右排序,对于菜单或列,则从上到下排序。我们也可以更改tabindex的值以匹配页面的布局,并确保实现最简单的导航。

如果你阅读过《使用tabindex的正确姿势》一文,你会发现tabindex设置的好对于改善用户体验是有益的,特别是对于那些过渡依赖于键盘操作的用户群体更是如此。但世间万物总是两极的,用的好就有益处,用的不好反而会有坏处。我们在使用tabindex可以按下面的规则来使用:

  • tabindex="-1":设置了tabindex-1的元素,该元素不会被置于页面的tab键序列中,但可以通过JavaScript的focus()获取,允许脚本设置元素的焦点。当你需要将焦点转移到通过脚本或用户操作之外更新的内容时,该设置非常方便
  • tabindex="0":可以让非常聚焦元素按自然顺序出现在tab键序列中
  • tabindex="1":不要设置tabindex="1"或任何大于0的值

对于tabindex仅仅是键盘导航中的一种,除了这个之外还有其他的一些键盘操作,比如控件中的导航选择当前项运行控制离开控制转到第一项或最后一项等操作。

导航控件

使用键盘上的Tab键来获取可聚焦元素(控件):

控件中的导航

可以使用键盘上的箭头键移动到控件中的选项,如菜单中的选择、命令栏中的命令或列表中的项。

选择当前项

可以使用键盘上的空格键选择或取消控件中得到焦点的项。

运行控件

按键盘上的EnterReturn键运行控件。

离开控件

按键盘上的Esc键奶出控件并返回到容器。

跳到第一项或最后一项

按键盘上的HomeEnd键直接跳到列表、菜单等控件中的第一项或最后一项。

事实上,对于Web应用来说,键盘设计(快捷键)要比Web页面更强很多,比如Chrome浏览器来说,在设计的时候就有很多键盘快捷键盘:

当然,如果你在构建Web页面的时候,想给元素添加一些快捷键,提供给高度依赖键盘操作的用户来说,也是有一定的方式方法的。

在Web开发当中,如果想给当前元素生成快捷键的话,可以像tabindex一样给该元素显式的设置accesskey属性。该属性的值必须包含一个可打印字符。

<a href="https://www.fedev.cn/" accesskey="h">Home</a>

正如上面示例所示,在不同系统不同的浏览器就可以配合acceesskey的值,用户将到达首页的链家。

浏览器 Windows Mac Linux
Chrome Alt + accesskey Control + Alt + accesskey Alt + accesskey
Firefox Alt + Shift + accesskey Control + Alt + accesskey Alt + Shift + accesskey
IE Alt + accesskey N/A N/A
Safari Alt + accesskey Control + Alt + accesskey N/A
Opear Alt + accesskey Control + Alt + accesskey Alt + accesskey

除了浏览器的支持之外,accesskey属性还有很多问题:

  • accesskey 值可能与系统或浏览器键盘快捷键或辅助技术功能相冲突。对于一个操作系统来说,辅助技术和浏览器组合可能无法与其他操作系统协同工作
  • 某些 accesskey 值可能不会出现在某些键盘上,特别是在国际化是一个问题的时候
  • 依赖于数字的 accesskey 值可能会让那些经历认知问题的人感到困惑,因为他们的数字与它触发的功能没有逻辑关联
  • 通知用户 accesskeys 存在,这样他们就能意识到该功能。如果没有公开这些信息的方法,accesskeys 可能会被意外激活

由于这些问题,一般建议不要在大多数通用的网站和Web应用程序中使用 accesskey 属性

有关于键盘和可访问性方面的更多知识还可以阅读下面这些文章:

设计更具包容性的表单

表单的设计是件非常复杂的事情,即使充分利用了视觉和动效的表现能力,也不一定能将表单设计的好。那么对于设计一个更具可访问性的表单更是件不易的事情。设计一个可访问性的表单可以显著改善用户体验,而且设计一个可访问性的表单不仅仅是设计师,交互的事情,在整个过程中也离不开开发。特别是在使用HTML构建一个表单时,就有很多事情可做。

fieldset适合对表单元素进行分组,并为它们提供更好的上下文

在设计表单时,可能遇到过必须向表单中添加多个单选按钮或复选框的情况。使用inputtype="radio"type="checkbox"很容易实现。但是,如果要对单选按钮或复选框进行分组时,应该选择哪个元素呢?

你可能首先会想到像div这样的容器元素。说实话,虽然在最终呈现给用户的面前效果是符合大家预期的,但对于可访问性并不是最合适的。面对这样的场景,我们不应该把表单中分组(分域)的元素fieldset给忘了,这是一个很理想的分组容器。

placeholder不是label

很多时候在设计表单时,很多人喜欢使用placeholder替代label

特别是在移动端更是如此,认为这样可以节省更多的空间。但这样做对于可访问性来说并不是件好事,特别是对于智力有障碍的用户。我们都知道,在使用placeholder的描述input时,当input得到焦点时,placeholder会自动消失,那么记忆力不好的人很容易忘了表单对应的是什么。就这方面来说,label标签始图要比placeholder强,另外label标签还有placeholder不具备的优势,比如说,label可以和input关联起来。在一起一交互上是有益的,如下图所示:

当然,在交互设计上可以做一些优化,让label标签除了起到自身的作用之外,还可以让其充当是placeholder。在社区中,常常把这种交互方式称为Float Label Pattern

如果你对labelplaceholder相互之间的讨论感兴趣,还可以花点时间阅读下面相关的文章:

尽可能的使用原生的表单元素

在HTML中为表单制作提供了不少的元素标签,这些原生的表单元素在不同的系统不同的浏览器中渲染的效果和交互行为都会有所差异。虽然不少同学为了迎合设计上的需求,会采用类似div的标签来模拟一些表单控件,比如单选按钮复选框按钮下拉菜单进度条等。这些控件在UI上是满足了视觉要求,但对于可访问性可是致命的,所以建议大家在构建表单时,尽可能的避开使用非表单元素来模拟表单控件。

另外,表单元素还有自己独特的特性,即表单元素有自己属性。比如input来说,不同的type类型值在移动端上能唤起不同的键盘,这是其他元素无法匹敌的(即使有技术手段能做到,但成本是倍涨):

不要遗忘表单元素的属性

构建表单时会需要对表单进行验证,在HTML中表单中很多元素有自己的属性可以很好的对表单进行验证,比如requiredminlengthpattern等。当用户在输入出错时,表单默认就会有一个很好的提示,如下图所示:

使用原生表单元素自带的属性对表单进行验证,除了省事之外,对于Web的可访问性也非常有益。

有关于表单和可访问性方面更多的介绍,可以阅读下面这些文章:

在必要时使用ARIA

W3C发布的WAI-ARIA是一套全面的技术规范,用于将可访问性信息添加到HTML元素中。根据WAI-ARIA规范将ARIA的角色、状态等添加到HTML元素上可以帮助屏幕阅读器用户与元素交互。

WAI-ARIA规范是一个很大的体系,主要涵盖了:

  • 角色(Roles):用来定义一个元素做什么
  • 属性(Properties):用来给元素添加额外的语义或信息
  • 状态(States):用来定义元素的当前状况

虽然WAI-ARIA规范可以让Web页面或应用更具可访问性,也可以更好的帮助开发者在使用无语义的标签元素时可以具有角色、语义和状态等功能。但这并不代表者WAI-ARIA可以取替具有语义化的原生HTML元素。

WAI-ARIA对于我们构建Web可访问性时可以锦上添花,但是运用不好反而是画蛇添足。这也印证了那句古话:“世间万物都是相生相克的”。作为我们开发者而言,想要的是锦上添花,那么我们就需要掌握WAI-ARIA方面的知识。只有掌握得越多,我们才能做得更好,才不至于画蛇添足。

有关于WAI-ARIA方面的知识较多,一两句也扯不清楚,在后续将会和大家花更多的时间一起来探讨这方面的知识。如果你急着想了解的话,可以先阅读:

小结

HTML虽是一门标记性的语言,时常被开发者忽略,特别是现在基于JavaScript框架开发的年代。虽然如此,但是要给Web页面或应用构建一个较好的HTML结构并不是件易事。如果我们专注于Web可访问性,那么有很多的细节需要我们去考虑(可能你平时不太注意这方面的细节)。正如文章中提到的,使用语义化的标签、怎么使用原生的标签元素、又是如何给标签元素添加合适的属性等。除此之外,还可以借助WAI-ARIA来增强可访问性的建设,即使你不太了解这些,我们可以参考A11Y Project中提供的检查清单针对自己的应用做相应的检测。随着后续的学习,我们还可以借助工具来帮助我们做检测,让我们构建的Web应用更具可访问性。

最初我没想到HTML对于可访问性有这么大的帮助(我一直以为可访问性是WAI-ARIA做的事情),其实并非如此,除了我们今天聊到的,应该还有可以更多的细节,如果你在这方面有经验,欢迎在下面的评论中与我们一起分享。