写给Web开发者使用的无障碍设计指南

发布于 大漠

这一年多来一直在探讨Web可访问性相关的知识,在小站上也陆续输出了一些有关于Web可访问性相关的技术文章。构建Web可访问性应用或页面涉及到的知识领域绝不仅是某个方面,从设计开始,到Web开发中的HTMLCSS、JavaScript各方面都有,甚至领域还可以更小,比如颜色工具ARIA以及一些属性的使用。但是对于很多从未接触过Web可访问性开发的同学来说,他还是会感到困惑。那么今天这篇访问就是来帮助开发者解惑的。因为文章主要就是和大家一起探讨,Web开发者可以根据下面提到的内容或者说手段来对Web可访问性进行优化。如果你对这方面感兴趣的话,请继续往下阅读。

给图像提供可替代文本

构建Web应用或页面时,需要确保图像的替代文本添加到所有信息笔功能图像中。在Web中使用图像主要有两种方式,其一是HTML中使用<img>标签引入图像,其二是CSS给元素添加背景图像。

如果我们使用<img>标签给Web应用添加图像的话,需要使用alt属性给无法看到图像的人,包括使用屏幕阅读器或盲文输出设备的人,提供访问图像内容的途径。大多数文档格式都支持alt文本,比如:

<img src="figure1.png" alt="Figure 1. Percentages of images with alt text" longdesc="figure1-longdesc.html" />

但在Web应用中,有些图像是纯装饰的。如果一个图像是纯装饰的,可以考虑下面几种方式来告诉屏幕阅读器:

  • 避免使用HTML的<img>,更建议使用CSS的background-image将图像引入到Web应用中
  • 如果在HTML中使用了<img>,应该给alt属性添加一个空字符串,比如alt=" "
  • 如果在HTML中使用了<img>,并且给alt设置了具体的描述信息,应该在<img>元素上或其父容器上显式设置role="presentation"aria-hidden="true"

有关于这方面更深入的探讨还可以阅读:

在Web中除了使用<img>或CSS的background-image引入图像之外,还有图标的应用。Web中图标的使用除了imgbackground-image引入之外,还有其他的使用方式,比如说:Icon Font图标SVG图标。那么在使用这些方式构建Web图标的时候,我们也需要为其可访问性做考虑,只不过处理方式有多种,比如字体图标:

<!-- Font Awesome -->
<a href="path/to/shopping/cart" aria-label="View 3 items in your shopping cart">
    <i aria-hidden="true" class="fas fa-shopping-cart"></i>
</a>

<a href="path/to/shopping/cart">
    <i aria-hidden="true" class="fas fa-shopping-cart"></i>
    <span class="sr-only">View 3 items in your shopping cart</span>
</a>

/* CSS */
.sr-only {
    border: 0 !important;
    clip: rect(1px, 1px, 1px, 1px) !important;
    clip-path: inset(50%) !important;
    height: 1px !important;
    margin: -1px !important;
    overflow: hidden !important;
    padding: 0 !important;
    position: absolute !important;
    width: 1px !important;
    white-space: nowrap !important;
}

.sr-only-focusable:focus,
.sr-only-focusable:active {
    clip: auto !important;
    clip-path: none !important;
    height: auto !important;
    margin: auto !important;
    overflow: visible !important;
    width: auto !important;
    white-space: normal !important;
}

对于SVG图标,我们可以在<svg>中引入<title>元素,甚至也可以像上面的字体图标一样给SVG图标添加可替代文本信息:

<!-- SVG -->
<svg  class="svg-inline--fa fa-magic fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
    <title id="svg-inline--fa-title-magic">Magic is included!</title>
    <path fill="currentColor" d="M101.1 505L7 410.9c-9.4-9.4-9.4-24.6 0-33.9L377 7c9.4-9.4 24.6-9.4 33.9 0l94.1 94.1c9.4 9.4 9.4 24.6 0 33.9L135 505c-9.3 9.3-24.5 9.3-33.9 0zM304 159.2l48.8 48.8 89.9-89.9-48.8-48.8-89.9 89.9zM138.9 39.3l-11.7 23.8-26.2 3.8c-4.7.7-6.6 6.5-3.2 9.8l19 18.5-4.5 26.1c-.8 4.7 4.1 8.3 8.3 6.1L144 115l23.4 12.3c4.2 2.2 9.1-1.4 8.3-6.1l-4.5-26.1 19-18.5c3.4-3.3 1.5-9.1-3.2-9.8L160.8 63l-11.7-23.8c-2-4.1-8.1-4.1-10.2.1zm97.7-20.7l-7.8 15.8-17.5 2.6c-3.1.5-4.4 4.3-2.1 6.5l12.6 12.3-3 17.4c-.5 3.1 2.8 5.5 5.6 4L240 69l15.6 8.2c2.8 1.5 6.1-.9 5.6-4l-3-17.4 12.6-12.3c2.3-2.2 1-6.1-2.1-6.5l-17.5-2.5-7.8-15.8c-1.4-3-5.4-3-6.8-.1zm-192 0l-7.8 15.8L19.3 37c-3.1.5-4.4 4.3-2.1 6.5l12.6 12.3-3 17.4c-.5 3.1 2.8 5.5 5.6 4L48 69l15.6 8.2c2.8 1.5 6.1-.9 5.6-4l-3-17.4 12.6-12.3c2.3-2.2 1-6.1-2.1-6.5l-17.5-2.5-7.8-15.8c-1.4-3-5.4-3-6.8-.1zm416 223.5l-7.8 15.8-17.5 2.5c-3.1.5-4.4 4.3-2.1 6.5l12.6 12.3-3 17.4c-.5 3.1 2.8 5.5 5.6 4l15.6-8.2 15.6 8.2c2.8 1.5 6.1-.9 5.6-4l-3-17.4 12.6-12.3c2.3-2.2 1-6.1-2.1-6.5l-17.5-2.5-7.8-15.8c-1.4-2.8-5.4-2.8-6.8 0z"></path>
</svg>

<!-- aria-label -->
<svg  class="svg-inline--fa fa-magic fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" aria-label="Magic is included!">
    <path fill="currentColor" d="M101.1 505L7 410.9c-9.4-9.4-9.4-24.6 0-33.9L377 7c9.4-9.4 24.6-9.4 33.9 0l94.1 94.1c9.4 9.4 9.4 24.6 0 33.9L135 505c-9.3 9.3-24.5 9.3-33.9 0zM304 159.2l48.8 48.8 89.9-89.9-48.8-48.8-89.9 89.9zM138.9 39.3l-11.7 23.8-26.2 3.8c-4.7.7-6.6 6.5-3.2 9.8l19 18.5-4.5 26.1c-.8 4.7 4.1 8.3 8.3 6.1L144 115l23.4 12.3c4.2 2.2 9.1-1.4 8.3-6.1l-4.5-26.1 19-18.5c3.4-3.3 1.5-9.1-3.2-9.8L160.8 63l-11.7-23.8c-2-4.1-8.1-4.1-10.2.1zm97.7-20.7l-7.8 15.8-17.5 2.6c-3.1.5-4.4 4.3-2.1 6.5l12.6 12.3-3 17.4c-.5 3.1 2.8 5.5 5.6 4L240 69l15.6 8.2c2.8 1.5 6.1-.9 5.6-4l-3-17.4 12.6-12.3c2.3-2.2 1-6.1-2.1-6.5l-17.5-2.5-7.8-15.8c-1.4-3-5.4-3-6.8-.1zm-192 0l-7.8 15.8L19.3 37c-3.1.5-4.4 4.3-2.1 6.5l12.6 12.3-3 17.4c-.5 3.1 2.8 5.5 5.6 4L48 69l15.6 8.2c2.8 1.5 6.1-.9 5.6-4l-3-17.4 12.6-12.3c2.3-2.2 1-6.1-2.1-6.5l-17.5-2.5-7.8-15.8c-1.4-3-5.4-3-6.8-.1zm416 223.5l-7.8 15.8-17.5 2.5c-3.1.5-4.4 4.3-2.1 6.5l12.6 12.3-3 17.4c-.5 3.1 2.8 5.5 5.6 4l15.6-8.2 15.6 8.2c2.8 1.5 6.1-.9 5.6-4l-3-17.4 12.6-12.3c2.3-2.2 1-6.1-2.1-6.5l-17.5-2.5-7.8-15.8c-1.4-2.8-5.4-2.8-6.8 0z"></path>
</svg>

<!-- aria-label -->
<svg  class="svg-inline--fa fa-magic fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" aria-labelledby="svg-title">
    <title id="svg-title">Magic is included!</title>
    <path fill="currentColor" d="M101.1 505L7 410.9c-9.4-9.4-9.4-24.6 0-33.9L377 7c9.4-9.4 24.6-9.4 33.9 0l94.1 94.1c9.4 9.4 9.4 24.6 0 33.9L135 505c-9.3 9.3-24.5 9.3-33.9 0zM304 159.2l48.8 48.8 89.9-89.9-48.8-48.8-89.9 89.9zM138.9 39.3l-11.7 23.8-26.2 3.8c-4.7.7-6.6 6.5-3.2 9.8l19 18.5-4.5 26.1c-.8 4.7 4.1 8.3 8.3 6.1L144 115l23.4 12.3c4.2 2.2 9.1-1.4 8.3-6.1l-4.5-26.1 19-18.5c3.4-3.3 1.5-9.1-3.2-9.8L160.8 63l-11.7-23.8c-2-4.1-8.1-4.1-10.2.1zm97.7-20.7l-7.8 15.8-17.5 2.6c-3.1.5-4.4 4.3-2.1 6.5l12.6 12.3-3 17.4c-.5 3.1 2.8 5.5 5.6 4L240 69l15.6 8.2c2.8 1.5 6.1-.9 5.6-4l-3-17.4 12.6-12.3c2.3-2.2 1-6.1-2.1-6.5l-17.5-2.5-7.8-15.8c-1.4-3-5.4-3-6.8-.1zm-192 0l-7.8 15.8L19.3 37c-3.1.5-4.4 4.3-2.1 6.5l12.6 12.3-3 17.4c-.5 3.1 2.8 5.5 5.6 4L48 69l15.6 8.2c2.8 1.5 6.1-.9 5.6-4l-3-17.4 12.6-12.3c2.3-2.2 1-6.1-2.1-6.5l-17.5-2.5-7.8-15.8c-1.4-3-5.4-3-6.8-.1zm416 223.5l-7.8 15.8-17.5 2.5c-3.1.5-4.4 4.3-2.1 6.5l12.6 12.3-3 17.4c-.5 3.1 2.8 5.5 5.6 4l15.6-8.2 15.6 8.2c2.8 1.5 6.1-.9 5.6-4l-3-17.4 12.6-12.3c2.3-2.2 1-6.1-2.1-6.5l-17.5-2.5-7.8-15.8c-1.4-2.8-5.4-2.8-6.8 0z"></path>
</svg>

<button>
    <svg  class="svg-inline--fa fa-magic fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" focusable="true" aria-hidden="true">
        <path fill="currentColor" d="M101.1 505L7 410.9c-9.4-9.4-9.4-24.6 0-33.9L377 7c9.4-9.4 24.6-9.4 33.9 0l94.1 94.1c9.4 9.4 9.4 24.6 0 33.9L135 505c-9.3 9.3-24.5 9.3-33.9 0zM304 159.2l48.8 48.8 89.9-89.9-48.8-48.8-89.9 89.9zM138.9 39.3l-11.7 23.8-26.2 3.8c-4.7.7-6.6 6.5-3.2 9.8l19 18.5-4.5 26.1c-.8 4.7 4.1 8.3 8.3 6.1L144 115l23.4 12.3c4.2 2.2 9.1-1.4 8.3-6.1l-4.5-26.1 19-18.5c3.4-3.3 1.5-9.1-3.2-9.8L160.8 63l-11.7-23.8c-2-4.1-8.1-4.1-10.2.1zm97.7-20.7l-7.8 15.8-17.5 2.6c-3.1.5-4.4 4.3-2.1 6.5l12.6 12.3-3 17.4c-.5 3.1 2.8 5.5 5.6 4L240 69l15.6 8.2c2.8 1.5 6.1-.9 5.6-4l-3-17.4 12.6-12.3c2.3-2.2 1-6.1-2.1-6.5l-17.5-2.5-7.8-15.8c-1.4-3-5.4-3-6.8-.1zm-192 0l-7.8 15.8L19.3 37c-3.1.5-4.4 4.3-2.1 6.5l12.6 12.3-3 17.4c-.5 3.1 2.8 5.5 5.6 4L48 69l15.6 8.2c2.8 1.5 6.1-.9 5.6-4l-3-17.4 12.6-12.3c2.3-2.2 1-6.1-2.1-6.5l-17.5-2.5-7.8-15.8c-1.4-3-5.4-3-6.8-.1zm416 223.5l-7.8 15.8-17.5 2.5c-3.1.5-4.4 4.3-2.1 6.5l12.6 12.3-3 17.4c-.5 3.1 2.8 5.5 5.6 4l15.6-8.2 15.6 8.2c2.8 1.5 6.1-.9 5.6-4l-3-17.4 12.6-12.3c2.3-2.2 1-6.1-2.1-6.5l-17.5-2.5-7.8-15.8c-1.4-2.8-5.4-2.8-6.8 0z"></path>
    </svg>
    <span class="sr-only">Magic is included!</span>
</button>

正确使用标题

Web页面的大纲很多时候是有HTML中的标题标签(<h1> ~ <h6>)来组建,文档大纲可以用来划分文档并使用这些划分创建具有清晰层次结构。如下图所示:

创建一个健全的文档大纲对于构建可访问性Web应用很重要。这是因为屏幕阅读器并不只是通过从上到下阅读页面上的内容,还有其他的导航方式,例如列出所有的标题,然后直接跳到一个特定的标题。换句话说,屏幕阅读器可以获得所有标题的列表,级别是用标题的文本来宣布的,以便用户更易于理解页面层次结构。

因此,我们在构建Web应用的时候应该正确的使用标题。WCAG 2.1为标题的wgett提供了相应的指南:

  • A级标准:确保现有可见标题被正确使用,其级别反映了标题在标题层次结构中的相对顺序
  • AA级标准:确保标题的描述性文字的质量,而不是标题的存在
  • AAA级标准:确保网页的章节在被组织成章节时有标题

因此,如果我们只是想达到AA级别,已有的文本在视觉上允当标题的话,我们就应该使用正确的标题标签,并确保标题文本是描述性的。另外,在使用标题标签时,应该遵循两个基本原则:

  • 同一个页面只有一个<h1>
  • 不能在上升的过程中跳过一级

对于第一条规则很好理解,这里来解释一下第二条规则。

正如前面的截图所示,标题的层次是一级一级往下的,比如说,你要到达<h4>,就必须先从<h1><h2><h3>,不能直接从<h2>跳到<h4>。比如:

上图中橙色标注的都是丢失的标题,即标题有跳级现象,也就是违背了第二条规则。而且有的有多个<h1>标题,这也违背了原则一。

比如说,我们从<h1><h2><h3>再到<h4>这是正常的也是符合构建文档大纲的结构,但是,你可以从<h4>跳过<h3>直接跳到<h3>,如下图所示:

我一向提倡构建Web应用的时候,应该尽可能的使用具有语义化的标签元素。如果你因为某种元素未使用语义化标签又想让用户代理(比如屏幕阅读器)也能向用户呈现标题级别,那就需要使用ARIA中的role="heading"aria-level属性来声明:

<div role="heading" aria-level="1"></div> › <h1></h1> 
<div role="heading" aria-level="2"></div> › <h2></h2> 
<div role="heading" aria-level="3"></div> › <h3></h3> 
<div role="heading" aria-level="4"></div> › <h4></h4> 
<div role="heading" aria-level="5"></div> › <h5></h5> 
<div role="heading" aria-level="6"></div> › <h6></h6>

如果你想深入的了解文档大纲相关的内容,还可以阅读下面这些教程:

避免使用空链接和按钮

链接按钮 是Web中常见的交互元素。在某些情况之下,Web上的链接看上去像按钮,按钮像是链接

不过我们这里不讨论它是按钮还是链接这个话题,如果你对该话题感兴趣的话,可以花点时间阅读《它是按钮还是链接?》一文。我们要讨论的是,关于链接和按钮的可访问性的一个更具有挑战性的问题,即 链接和按钮文本本身

我们以链接为例吧。好的链接文本可以帮助个人理解每个链接的目的,这样就可以帮助用户做出相应的决策。这不是一个简单的可访问性问题,而是一个全面的可用性问题。然而,我们经常看到链接的文本是“更多”,“点击这里”,“了解更多”,“阅读全文”等,而没有附加的上下文内容。

为了确保链接或文本更具可访问性(或更具可用性),那么我们在构建链接或按钮时,就需要提供更具上下文的文本信息,以确保符合WCAG规范:

  • A:每个链接(或按钮)的目的可以单独从链接(或按钮)文本和通过程序确定的链接(或按钮)上下文来确定,除非链接(或按钮)的目的对一般用户来说是不明确的
  • AAA级:每个链接(或按钮)的目的只能从链接文本中分辩出来,断章取义的链接(或按钮)也能被理解

在我看来,A级的成功标准还不足以确保一个链接(或按钮)的上下文能够被有效地表述出来,以达到正确的理解。

比如在博客列表中,相邻标题提供了附加的链接上下文,但没有任何程序性关联:

对应的结构很简单:

<h2>文章标题</h2>
<p>文章摘要描述</p>
<a href="">阅读全文</a>

就该示例而言,当一个使用屏幕阅读器的盲人在链接上获得焦点时,没有合适的上下文,他们需要浏览和阅读周围的文本,或者在这种情况下,浏览到前面的标题,以确定这个“阅读全文”链接的上下文。如果这个模式在页面中重复多次,那么对于使用屏幕阅读器的用户来说就会变得非常沮丧。

要真正使链接(或按钮)可访问,我们需要在链接(或按钮)中默认提供上下文。最简单的方法是在<a>(或<button>)元素内添加描述链接(或按钮)的文本的上下文信息:

<a href="">阅读全文 <span class="sr-only">关于《A11Y:如何使用WAI-ARIA增强Web可访问性》一文</span></a>
<button>阅读全文 <span class="sr-only">关于《A11Y:如何使用WAI-ARIA增强Web可访问性》一文</span></button>

虽然这是首选的方法,但是在某些情况下,这可能会在视觉上让人难以承受。在这种情况之下,可以简单地在.sr-only使用CSS,用于视觉上隐藏,但屏幕阅读器上不隐藏。

注意,Web上使用CSS或者其他姿势隐藏元素有很多种,不同的姿势对用户代理可视上有较大差异,如果你对这方面感兴趣的话,可以阅读《Web隐藏术》一文。

示例中,在链接(或按钮)前面有标题,比如<h1>,我们还可以使用aria-labelledby属性将链接(或按钮)关联起来。当使用现有文本来构建上下文链接(或按钮)文本时,你将需要考虑措辞的质量。因此,我们可以像下面这样给链接(或按钮)添加相关联的上下文描述信息:

<section>
    <h2 id="a11y__headding">A11Y:如何使用WAI-ARIA增强Web可访问性</h2>
    <p>很多Web开发者一直都认为使用WAI-ARIA是增强Web可访问性的最佳方式,但是对于了解或者说经常构建可访问性Web应用的开发同学来说,WAI-ARIA其实对于Web可访问性来说是一种“毒药”。</p>
    <p><a id="a11y__link" class="btn btn-success" href="#" aria-labelledby="a11y__link a11y__headding">阅读全文</a></p>
</section>

iOS的“VoiceOver”会像下面这个视频一样给用户呈现信息:

在ARIA中,除了aria-labelledby属性之外,还有aria-labelaria-describedby可以增强文本上下文信息,有关于这方面更详细的介绍,可以阅读《聊聊aria-labelaria-labelledbyaria-describedby》一文。

在Web应用的开发中,很多时候,链接(或按钮)除了纯文本之外,还会有纯图标的链接(或按钮),也还会有文本和图标同时出现在链接(或按钮)的场景。对于Web可访问性来说,不同的方式有着不同的处理方式,有些细节也有所不同。前面我们一起聊了纯文本的链接(或按钮)我们应该有更具上下文的文本信息,对于纯图标链接(或按钮)或文本和图标都有的链接(按钮),我们需要稍做差异性的处理:

  • 对于纯图标的链接(或按钮),需要提供一定的文本信息
  • 对于文本加图标的链接(或按钮),需要对图标做相应的隐藏处理

对于很多不关注Web可访问性的开发者而言,他们并不会太关注这方面的细节,因此在构建的时候也不会考虑Web可访问性方面的细节。很多时候你看到的会像下面这样:

<a href="/">
    <svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="200" height="200">
        <path d="M640 790.528H297.504v-0.096l86.496 0.032v-118.88c0-0.8 0.224-1.536 0.224-2.304 1.28-69.6 57.984-125.888 127.776-125.888s126.496 56.32 127.776 125.888c0 0.768 0.224 1.504 0.224 2.304v118.944z m240.896-427.68L800 309.856V207.168a32 32 0 1 0-64 0v60.8l-206.464-135.328A31.296 31.296 0 0 0 511.424 128a31.168 31.168 0 0 0-17.6 4.64L142.464 362.88a32 32 0 0 0 35.072 53.536L192 406.912V800c0 30.08 27.168 54.592 60.576 54.592h518.848C804.832 854.56 832 830.08 832 800V407.36l13.856 9.056a31.968 31.968 0 0 0 35.04-53.536z" fill="currentColor"></path>
    </svg>
</a>

<!-- 或 -->
<a href="/">
    <svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="200" height="200">
        <path d="M640 790.528H297.504v-0.096l86.496 0.032v-118.88c0-0.8 0.224-1.536 0.224-2.304 1.28-69.6 57.984-125.888 127.776-125.888s126.496 56.32 127.776 125.888c0 0.768 0.224 1.504 0.224 2.304v118.944z m240.896-427.68L800 309.856V207.168a32 32 0 1 0-64 0v60.8l-206.464-135.328A31.296 31.296 0 0 0 511.424 128a31.168 31.168 0 0 0-17.6 4.64L142.464 362.88a32 32 0 0 0 35.072 53.536L192 406.912V800c0 30.08 27.168 54.592 60.576 54.592h518.848C804.832 854.56 832 830.08 832 800V407.36l13.856 9.056a31.968 31.968 0 0 0 35.04-53.536z" fill="currentColor"></path>
    </svg>
    首页
</a>

第一个链接没有任何的上下文文本信息,用户并不知道他是一个代表“首页”,第二个链接虽然有“首页”上下文相关的描述信息,但<svg>图标也能得到相应的焦点,会造成焦点冗余,对于依赖于屏幕阅读器的用户而言,体验也不好。

注意,示例中的<svg>有可能也是<img>元素,也有可能是字体图标,比如<i class="fa fa-home"></i>

就上面的示例而言,为了让带有图标(或纯图标)的链接(或按钮),我们需要给使用ARIA让它更具可访问性,比如:

<a href="/" aria-label="首页">
    <svg focusable="true" aria-hidden="true" role="img" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="200" height="200">
        <path d="M640 790.528H297.504v-0.096l86.496 0.032v-118.88c0-0.8 0.224-1.536 0.224-2.304 1.28-69.6 57.984-125.888 127.776-125.888s126.496 56.32 127.776 125.888c0 0.768 0.224 1.504 0.224 2.304v118.944z m240.896-427.68L800 309.856V207.168a32 32 0 1 0-64 0v60.8l-206.464-135.328A31.296 31.296 0 0 0 511.424 128a31.168 31.168 0 0 0-17.6 4.64L142.464 362.88a32 32 0 0 0 35.072 53.536L192 406.912V800c0 30.08 27.168 54.592 60.576 54.592h518.848C804.832 854.56 832 830.08 832 800V407.36l13.856 9.056a31.968 31.968 0 0 0 35.04-53.536z" fill="currentColor"></path>
    </svg>
</a>

<!-- 或 -->
<a href="/">
    <svg focusable="true" aria-hidden="true" role="img" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="200" height="200">
        <path d="M640 790.528H297.504v-0.096l86.496 0.032v-118.88c0-0.8 0.224-1.536 0.224-2.304 1.28-69.6 57.984-125.888 127.776-125.888s126.496 56.32 127.776 125.888c0 0.768 0.224 1.504 0.224 2.304v118.944z m240.896-427.68L800 309.856V207.168a32 32 0 1 0-64 0v60.8l-206.464-135.328A31.296 31.296 0 0 0 511.424 128a31.168 31.168 0 0 0-17.6 4.64L142.464 362.88a32 32 0 0 0 35.072 53.536L192 406.912V800c0 30.08 27.168 54.592 60.576 54.592h518.848C804.832 854.56 832 830.08 832 800V407.36l13.856 9.056a31.968 31.968 0 0 0 35.04-53.536z" fill="currentColor"></path>
    </svg>
    首页
</a>

也就是说要在<svg><img><i class="fa fa-home">元素上显式的添加focusable="true" aria-hidden="true"相关的属性设置。另外,对于<svg><i class="fa fa-home">设置role="img"会更适合一点。

使用ARIA地标

在WAI-ARIA中角色 role 的介绍中就有地标角色,即 Landmark ,他们和HTML5的 <main><header><nav><aside><footer> 有着相应的对应关系。

这允许我们给站点中的元素增加我们想要的语义属性。第一个主要区域便是用于为屏幕阅读器提供信息,以便用户可以找到常见的页面元素。

<header>
    <h1>标题</h1>
    <nav>
        <ul>
            <li>
                <a href="">CSS</a>
            </li>
            <li>
                <a href="">HTML</a>
            </li>
        </ul>
        <form>
            <label for="search">搜索</label>
            <input type="search" id="search" />
        </form>
    </nav>
</header>

<main>
    <article>
        <h2>文章标题</h2>
    </article>
    <aside>
        <h2>AD</h2>
    </aside>
</main>

<footer>
    <p>版本归属</p>
</footer>

“VoiceOver”呈现给用户的效果如下:

如果你转到VoiceOver的地标菜单(使用VoiceOver 绑定键(你可以在VoiceOver Utility 中设置) + U 访问,然后使用光标或者键盘来选择菜单选项),你将看到大部分元素都已很好地列出,因此可以快速访问它们。

尽管如此,我们可以做的更好。这个搜索表单是一个人们愿意找到的重要的地标,我们可以设置 input 的类别为 search (<input type="search">)。 另外,在一些老的浏览器(尤其是IE8) 是无法识别的这些HTML5 的元素语义化的。

让我们来优化上文代码并且用上无障碍特性。首先我们给HTML 的结构加上角色。

<header role="banner">
    <h1>标题</h1>
    <nav role="navigation">
        <ul>
            <li>
                <a href="">CSS</a>
            </li>
            <li>
                <a href="">HTML</a>
            </li>
        </ul>
        <form role="search">
            <input type="search" name="search" placeholder="搜索查询" aria-label="通过网站内容进行搜索" />
        </form>
    </nav>
</header>

<main role="main">
    <article role="article">
        <h2>文章标题</h2>
    </article>
    <aside role="complementary">
        <h2>AD</h2>
    </aside>
</main>

<footer role="contentinfo">
    <p>版本归属</p>
</footer>

我们用了一个额外的功能:input元素用了属性 aria-label, 它给它一个描述性标签,可以由屏幕阅读器读出,尽管我们没有 label 元素。在这些情况下,这非常有用——像这样的搜索表单是一个非常常见的,易于识别的功能,添加 label 会破坏页面设计。

再用VoiceOver时,呈现给用户的效果如下:

如果由于某种原因,你的网站仅使用

构建,那么你肯定很需要用 ARIA 角色以提供所需的语义!

将标签<label>和表单控件关联在一起

表单的使用是Web中的一个重点,那么表单无障碍设计的好与坏直接会影响用户是否能继续往下操作。比如登录、注册,比如购物下单,修改地址等。

在设计表单时,表单的控件除 type="button"input 和带有 hidden 属性的 input 可以没有对应的 <label> 标签外,其他的表单控件都应该有一个对应的可读的标签( <label> )。

label之所以如此重要,是因为ATs(比如屏幕阅读器)技术使用它们来向用户展示所关注领域的目的。没有它,用户将无法理解他们应该在对应的input中输入什么。

此外,可以看到的label对其他用户也很有帮助,因为浏览器倾向于自动填充表单。这将隐藏字段中提供的标签,并让用户怀疑表单是否被正确地自动填充。标签总是比代表相同的图标更易理解。

对于复选框(<input type="checkbox">)和单选按钮(<input type="radio">),最好提供关于整个组的用途的信息,而不是单个选项。针对于该场景,可以使用<fieldset><legend>元素会更好些:

<fieldset>
    <legend>My favorite foods</legend>
    <label>
        <input type="checkbox" name="foods" value="pizza"> Pizza
    </label>
    <label>
        <input type="checkbox" name="foods" value="burger"> Burger
    </label>
    <label>
        <input type="checkbox" name="foods" value="pasta"> Pasta
    </label>
</fieldset>

在编写HTML时,<input>除了被<label>包裹起来之外,还可以通过labelforinputid来联动,比如:

<label for="firstname">First name:</label> 
<input type="text" id="firstname" />

如果使用WAI-ARIA,还可以通过label标签的idinputaria-labelledby联动,比如:

<label id="firstname">First name:</label> 
<input type="text" aria-labelledby="firstname" />

如果不能使用label时,可以使用aria-label或者在input上使用title(在input上不太使用title属性)。

将相关的表单字段分组在一起

Web中表单设计是非常重要的,表单设计的好不好对于用户来说非常的重要。其中表单设计中 将相关的表单字段 分组在一起就是细节之一。比如下图就从UX的角度向大家展示了右边的表单设计在用户体验上强于左侧的表单设计:

右侧和左侧相比,最大的差异就是右侧的表单将相关的表单字段分在同一组中。

在HTML中,可以使用<fieldset><lenged>将相同字段的表单包裹在一起。其实前面的示例已经向大家展示过了:

<fieldset>
    <legend>My favorite foods</legend>
    <label>
        <input type="checkbox" name="foods" value="pizza"> Pizza
    </label>
    <label>
        <input type="checkbox" name="foods" value="burger"> Burger
    </label>
    <label>
        <input type="checkbox" name="foods" value="pasta"> Pasta
    </label>
</fieldset>

<fieldset>中放置了一组复选框(<input type="checkbox" />)。

要想设计或开发一套具有高可访问性和较好交互体验的表单,涉及到的不仅仅是<label><fieldset><legend>就能解决的,还会有很多属性,这些属性是HTML方面的,也有ARIA方面的,还有CSS方面的,当然也离不开设计方面的。如果你对这方面感兴趣的话,可以花一些时间阅读下面这些文章:

给文档设置语言类型

了确保正确的发音,屏幕阅读器为它们支持的每种语言使用不同的声音库。屏幕阅读器可以轻松地在这些语言库之间切换,但只有在Web页面指定为给定内容读取哪种语言的情况下才可以。

如果页面没有为 <html> 元素指定语言,那么屏幕阅读器就会假定页面使用用户在设置屏幕阅读器时选择的默认语言,这通常会导致无法理解内容。

比如说,<html> 未显式设置 lang 属性,Lighthouse 检测的时候就会抛出相应的错误信息:

曾经我就踩过这样的一个坑,在 <html> 没有设置 lang ,另外就是在中文Web页面设置了 lang 的值为 en ,结果iOS的“VoiceOver”朗读出来的信息是让用户听不懂,后来将 lang 的值设置为 zh-CN 才正常。

这个示例想提醒大家的是,在构建Web应用的时候,应该根据自己的应用设置正确的语言类型,比如中文简体,我们可以设置:

<html lang="zh-CN"> </html> 

应用到<html>元素,它告诉屏幕阅读器不会自动识别语言类型。如果在一个句子中切换语言,可以使用lang属性标记一个单词或短语:

<p>There's a certain <span lang="fr">je ne sais quoi</span> in the air.</p>

如果你不这样做,它可能会导致用户难以理解,屏幕阅读器可能会以英语口音发一个德语句子。

另外,我们还可以像下面这样做:

<p class="visually-hidden">Expressionist architecture is one of the three dominant styles of Modern…</p>    
<p aria-hidden="true">Expressionismus in einer der drei dominanten Stile der modernen Architektur…</p>

其中有一句是德语,但对屏幕阅读器来说是隐藏的。

添加足够的颜色对比度

颜色对比度是一个经常被忽视的问题(可访问性 ),从众多网站或应用程序的“无障碍”检测结果就可以发现“颜色对比度达不到WCAG标准”:

视弱的人可能会发现,如果背景颜色和前景颜色对比度低,难于阅读。而且从世界卫生组织在一份关于视力障碍和失明统计报告中可以得知有近2.7亿人患有中度至重度视力障碍。因此在设计一个Web应用时,需要考虑文本和背景之间颜色要有充分的对比度。

根据WCAG视觉对比(WCAG Visual Contrast)规范,我们可以得知,文本与背景之间的颜色对比度应该至少是 4.5:1(可达到级别 AA)。如果文本更宽,更大和更重,那么对于视力有一定障碍的用户更便于阅读,那么颜色上的对比度就可以稍微更低一点。比如,文本字号是18pt14px粗体时,最低对比度可以下降到 3:1

在设计的时候可以按照下面表格相关的标准来设计(根据规范整理):

文本字号 普通 加粗 AA AAA
小号 < 24px < 19px 4.5:1 7:1
大号 > 24px > 19px 3:1 4.5:1

WCAG视觉对比(WCAG Visual Contrast)规范可以帮助视力障碍用户更好地使用互联网产品。

如果你实在拿不准配色是否合理(Web安全颜色),你可以借助在线工具,比如Contrast Checker:

该工具是根据WCAG 2.0 guidelines for contrast accessibility标准来做的。比如下面图所展示的效果就是一个较好的效果:

如果你想获得更详细的分数,我建议你使用WebAIM颜色对比度检查器,此工具将计算不同对比度的级别(AAAAAA)中常规文本大小和较大文本大小的分数,同时可以更改颜色值实时查看结果。

或者使用浏览器开发者工具也可以来检查和调整CSS:

这里特别向大家推荐一款Chrome浏览器的插件VisBug,可以帮你对Web应用上的任意元素的颜色对比度进行检测。

Chrome浏览器有一款插件"High Contrast",它能够提高页面调色方案的对比度,通过此类工具来查看页面能够在配色的选择上助我们一臂之力。

令人感到惊讶的是,早在1992年就有人开始使用下图这样的公式来计算颜色的对比度:

有关于该公式更详细的计算可以阅读《Signs and color contrast》一文。

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

当你的应用需要和用户做一些重要交流的时候(比如有单的填写),不要仅把颜色作为视觉提示。这是因为低视力,特别是色盲的用户很难理解。比如下图所示:

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

当然完全色盲的用户群体也相当少见,患有这种缺陷的人眼里只有不同深浅的灰色。颜色感知的变化影响的整个视觉光谱,而不仅仅是一种颜色。

你的初衷可能是选择一种大部分有色彩障碍的人都能识别到的颜色,但由于有各种色弱,且每种色弱的情况有轻有重,要选出这样一种颜色相当困难。不过橘色和蓝色还算能满足大部分情况。这就是为什么互联网产品里蓝色如此常见。

对于这些用户群体,唯一的原则是:别仅用颜色区分。比如上面的表单控件,可以在设计表单时,表单验证环节除了增加颜色之外区分之外,还可以添加一些区分对错的图标,甚至还可以添加一些提示信息。如下图所示:

Facebook的注册表单在可访问性方面要比Twitter的注册表单更友好,特别是对于色盲的用户群体!

避免使用小字体

浏览器可以提供快捷键(比如crl + command + )来放大页面,比如:

开发者应该使用一个合理的默认字体大小(浏览器默认的font-size16px),然后,用户可以根据需要缩小它。

请注意,font-size大小为1em时使用默认的浏览器字体大小,因此是大多数文本字号的理想选择。但em的计算是复杂的,会根据文档结构,元素自身font-size来决定计算值。

值得一提的是,给Web提供更好的可访问性,在文本方面不仅仅局限于font-size的设置。如果我们要给Web提供更好的可读性,易读性,就会涉及到排版方面的相关知识,比如字体、行高、间距等一系列的问题:

@Marcella Jalbert在她的博文《How to design for readability – a guide to successful web typography》中对文本排版可读性和易读性做了深入的阐述。

另外,有关于文本对Web可访问性相关的影响,还可以阅读下面这些文章:

提供可见的焦点指示

在HTML中有很多元素被称为可聚焦元素,比如我们熟悉的<a><button><input><textarea><label>等:

HTML中可聚焦元素最大的特点就是,它们是自动插入到Tab键顺序中,并内置了键盘事件处理,无需开发者进行干预。但并非所有元素都是可聚焦元素;在你按Tab键在页面上循环跳转时,段落(<p>)、<div>以及各种其他页面元素不会获得焦点,这是设计使然。一般不需要聚焦无法与用户进行交互的元素。

可聚焦元素另一个特点就是,可聚焦元素在获得焦点时都有可见的焦点指示器。就拿Chrome浏览器来说,获得焦点时样式上有明显的变化:

可聚焦元素获得焦点时的指示器对于Web可访问性同样的很重要。因为焦点指示器可以帮助用户知道键盘的焦点正在哪个元素上。这里的关键是:

所有可聚焦元素(或组件)在被聚焦时都有一个可见的焦点指示器

“可见是一个相对的术语”,WCAG规范对这个可见焦点指示器也有明确的定义

文本字段中有明确的插入符;控件周围有明显的可见边框

比如下面这个示例:

使用Tab键操作上面的Demo,你看到的效果会像下面这样:

同样的,在一些辅助技术(比如屏幕阅读器)上也有焦点指示器,只不过样式上看上去有所差异:

在CSS中,我们可以使用一些伪类选择器,比如:focus:focus-within:focus-visible给可聚焦元素设置焦点指示器样式:

前面也提到过了,对于可聚焦元素,如果没有显式设置CSS样式,那么不同的浏览器对焦点指示器的样式也会有所差异。也基于这个原因,很多开发者粗暴的将焦点指示器的样式去掉了:

*,
*:focus {
    outline: none 0;
}

加上很多视觉设计师在视觉上也很讨厌默认焦点指示器样式,但很多时候对焦点元素指示器样式没有做额外的设计。这样对于视力正常的用户来说并无大碍,但对于强度依赖于键盘或其辅助技术的用户就很不友好了,因为这些用户不知道自己正处于哪个位置。

因此,为了Web可访问性,我们不应该直接去掉焦点指示器样式效果,如果需要更好的看的指示器效果,可以使用CSS的outlineborderbox-shadow等属性来美化。另外,CSS的伪类选择器:focus:focus-within:focus-visible也有一定的差异性(对不同用户操作方式有差异)。不过这里不做详细阐述,如果感兴趣,还可以阅读下面这些教程:

使用 tabindex

有运动障碍的人,依赖屏幕阅读器的盲人以及一些强度依赖于键盘操作的用户群体,他们访问Web页面或Web应用主要依赖于键盘来进行操作。

使用键盘操作,主要会依赖于键盘上的Tab键,除此之外还可以会依赖键盘上的方向键、确认键盘、甚至是一些设置好的快捷键。而其中最为主要的是Tab键,通过操作键盘的Tab键可以获得焦点,提示用户现在在应用中的哪个位置。

但HTML中也有不可聚焦元素,比如divspan等元素。但往往我们在实际开发中,又需要让这些不可聚焦的元素可以得到焦点。比如模态框组件,模态框在弹出的时候希望能得到一个焦点,但很多时候是使用div来做这个模态框。可div是一个不可聚焦元素,这个时候我们就需要使用HTML的tabindex属性。

tabindex属性是一个全局属性,既可以用于可聚焦元素上也可以用于不可聚焦元素上。其取值不同时效果也不一样:

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

虽然规范是上是这样定义的,但是到真正的客户端的表现还是有所差异的。从测试的结果来看:

  • 显式设置tabindex="0"的元素(不管是可聚焦元素还是非聚焦元素)都可能得到焦点
  • 显式设置了tabindex="-1"的元素都无法得到焦点
  • 除了Safari浏览器,只要是可聚焦的元素都可以正常得到焦点
  • 非可聚焦元素,显式设置了tabindex值,但只未赋予任何值的情况之下都无法正常获得焦点

虽然tabindex能让我们很好的得到焦点,可以用来管理焦点(可以给非聚焦元素设置聚焦行为),可以让运动障碍的用户群体友好的操作Web页面;但世间万物总是两极的,用的好就有益处,用的不好反而会有坏处。所以在实际使用的时候,要慎用。

有关于tabindex更详细的介绍可以阅读《使用tabindex的正确姿势》一文。

在Web开发中键盘操作不仅仅是Tab键的操作,还有EnterSpace和方和向键之类的。这些键盘的设置对于Web可访问也非常的重要,但要处理管理好这些键盘事件的设置之类的也是复杂的事情。如果你对这方面感兴趣的话,可以花点时间阅读下面这些文章:

尽可能使用文字,避免使用文字的图片

在Web开发中,总是避免不了使用图像设计的文字,比如:

面对这种场景的时候,我们可以像下面这样使用,让更多的用户代理能获取到相应的信息:

<header>
    <span class="sr-only">组队赢红包</span>
    <img src="path" alt="组队赢红包" aria-hidden="true" />
</header>

另外,很多时候,开发者会使用background-image来引用图像,即使是这样,我们也需要有相应的描述文案让一些用户代理能获取到相应的信息:

<!-- 不好的 -->
<header style="background-image: url(path)"></header>

<!-- 好的 -->
<header style="background-image: url(path)">
    <span class="sr-only">组队赢红包</span>
</header>

使用ARIA隐藏语义

在《Web隐藏术》一文中我们主要介绍了CSS中隐藏元素的一些技术方案。不同的方案,对不同用户代理表现也有所不同:

同样的,对于一些辅助技术(比如屏幕阅读器),有的时候也需要隐藏元素,特别是对于一些没有传达内容或语义的元素。为此,我们可以使用rolepresentationnone。比如下面这个示例,使用<ul><li>构建的列表,其中<li>元素仅表示形式,对应的<li>内的<a>是可聚焦的,如下所示:

<ul role="tablist">   
    <li role="presentation">     
        <a role="tab" href="#">Tab 1</a>   
    </li>   
    <li role="presentation">     
        <a role="tab" href="#">Tab 2</a>   
    </li>   
    <li role="presentation">     
        <a role="tab" href="#">Tab 3</a>   
    </li> 
</ul>

在ARIA中,还提供了一种利用 aria-hidden 属性将非视觉隐藏内容排除在辅助技术访问范围之外的机制。如果对元素应用该属性,实际上是将元素及其所有子项从无障碍树中移除。只有 aria-labelledbyaria-describedby 属性引用的元素例外。

<div class="deck">
    <div class="slide" aria-hidden="true">
        Sales Targets
    </div>
    <div class="slide">
        Quarterly Sales
    </div>
    <div class="slide" aria-hidden="true">
        Action Items
    </div>
</div>

在元素上使用aria-hidden="true"可以通过隐藏元素来提升使用辅助技术的用户体验,比如像下面这些场景:

  • 纯装饰性的内容,如图标、图片
  • 重复的内容,如重复的文本
  • 屏幕外或被折叠的内容,如菜单

需要注意的是,aria-hidden="true" 不应该被用在可聚焦的元素上。 而且,由于这个属性是可以被子元素继承的,它也不应该被用在可聚焦元素的父元素上。

如果父元素带有 aria-hidden="true" ,那么即使使用 aria-hidden="false" 也无法将该元素显示出来。

另外,表面上,aria-hidden="true"role="presentation"role="none" 很相似,因为这三者都有以下特性:

  • 根据辅助即使隐藏页面内容
  • 无法在可聚焦元素上使用
  • 无法在可互动元素的父级元素上使用

尽管有上面这些相同点,但是各个属性的意图是不同的。

  • aria-hidden="true" 会把整个元素从可访问性API中移除
  • role="presentation"role="none" 会将元素从语义上移除,仍然会将元素暴露给辅助技术

不过,在下面这样的场景不应该使用aria-hidden="true"

  • HTML的hidden属性被设置了
  • 祖先元素被display: none属性设置成不显示状态
  • 祖先元素被visibility: hidden属性设置成不显示状态

在以上三个场景中,元素已经被隐藏,从可访问树种移除了,无需再添加aria-hidden="true"属性。有关于这方面的讨论还可以阅读《Know your ARIA: 'Hidden' vs 'None'》一文。

动态更新

aria-live 允许开发者将某个页面部分标记为“活动”,其意义在于,无论处在什么页面位置,都应立即向用户传达更新,而不是在用户恰好探索该页面部分时再行传达。当元素具有 aria-live 属性时,包含它及其子项的页面部分称作活动区域。

例如,活动区域可以是因用户操作而出现的状态消息。 如果消息的重要性足以吸引视力正常用户的注意,也就足以吸引辅助技术用户的注意(通过设置其 aria-live 属性)。

将这个简单 div

<div class="status">你的信息已经发送了。</div>

与其“活动”版本进行比较:

<div class="status" aria-live="polite">你的信息已经发送了。</div>

aria-live 有三个允许值:politeassertiveoff

  • aria-live="polite":指示辅助技术在完成其当前执行的任何操作后提醒用户这一变化。 它非常适合在事情重要但并不紧急时使用,aria-live 大多作此用途
  • aria-live="assertive":指示辅助技术中断其正在执行的操作,立即提醒用户这一变化。 这仅适用于重要并且紧急的更新,例如“您的更改因服务器出错而未予保存;请刷新页面”这样的状态消息,或者因用户操作(如按步进器小部件上的按钮)而直接引发的输入字段更新
  • aria-live="off":指示辅助技术暂停 aria-live 中断

可以运用一些技巧来确保活动区域工作正常。

首先,您的 aria-live 区域多半应在初始页面加载时进行设置。这并非定规,但如果您在某个 aria-live 区域遇到困难,可能就是这个问题所致。

其次,不同的屏幕阅读器对不同类型变化的反应有所差异。 例如,可通过将子元素的 hidden 样式从 true 切换为 false,在某些屏幕阅读器上触发提醒。

其他兼容 aria-live 的属性可以帮助您优化活动区域发生变化时传达给用户的信息。

aria-atomic 表示传达更新时是否应将整个区域作为一个整体加以考虑。 例如,如果某个包括日、月和年的日期小部件具有 aria-live=truearia-atomic=true,并且用户使用的步进器控件只能更改月份值,则会再次读出日期小部件的完整内容。aria-atomic 的值可以是 truefalse(默认值)。

aria-relevant 表示应向用户提供哪些类型的更改。有一些选项可以单独使用,或以令牌列表形式使用:

  • additions,表示任何添加到活动区域的元素都是重要内容。 例如,向现有状态消息日志追加 span 意味着将把该 span 告知用户(假定 aria-atomicfalse
  • text,表示添加到任何子节点的文本内容都是重要内容。 例如,如果修改自定义文本字段的 textContent 属性,将向用户读出修改后的文本
  • removals,表示应将移除任何文本或子节点的情况传达给用户
  • all,意味着所有更改都是重要更改。不过,aria-relevant 的默认值是 additions text,这表示如果您不指定 aria-relevant,它会将对元素的任何添加动态告知用户,而这很可能是您最想获得的信息。

最后,aria-busy 允许您通知辅助技术它应暂时忽略对元素的更改(例如在加载内容时)。 一切就位后,aria-busy 应设置为 false,以使阅读器的工作正常化。

有关于动态更新更多的介绍可以阅读下面这些文章:

创建具有可访问性的交互组件

我们在构建Web应用或页面时会经常用到一些具有交互方式的组件,比如警告框,模态框,手风琴等。在构建这些组件的时候,我们应该让它们具有可访问性,并且还应该支持键盘进行操作,使用ARIA与AT(Assistive Technology)进行通信,并为没有最新AT的用户优雅降级。WAI-ARIA最佳实战中提供了一些常见的交互式组件的的示例,我们在开发类似组件的时候可以参考该文档,正确的编写具有可访问性的组件

待续...

上面罗列的是我想到的有关于可以帮助大家构建可访问性Web应用或页面时应该注意的技巧。我肯定还有很多没有想到的技巧,如查你在这方面有相关的经验,欢迎在下面的评论中与我们一起共享。

另外,A11Y Reesoureces提供了很多关于A11Y相关的资料,如果你感兴趣的话,可以阅读这个网站提供的相关资料: