05月25, 2016

【译】开发无障碍的Web组件 - 简介

原文:http://www.zcfy.cc/article/320

这篇文章的审校工作是由Mallory van Achterberg完成的。谢谢所有SitePointer的审校者,因为你们,SitePointer才能提供如此优质的内容。

上一篇文章中,我演示了如何创建一个有多选功能的Web组件。读者在评论区域提到了Web组件无障碍这个重要的话题。毋庸置疑地,如今Web无障碍非常重要。所以,让我们来看看无障碍的含义,以及如何提高Web组件的可访问性(通过一个真实的例子)。

本文中的代码示例将基于上一篇文章中的代码。你可以从GitHub仓库复制一份,也可以查看本文结尾的例子

提高Web组件可访问性的必要条件

组件的可访问性通常涉及以下方面:

  1. 标签语义化
  2. 键盘支持
  3. 视觉无障碍

让我们更深入地了解各个方面。

标签语义化

你应该听说过屏幕阅读器。屏幕阅读器通过大声播放屏幕上展示的内容,帮助盲人或弱视群体使用应用。现在有很多屏幕阅读器,例如Windows的NVDAJAWS,Chrome的 ChromeVox,以及OS X的VoiceOver

当元素获得焦点时,屏幕阅读器会将该元素的相关信息朗读给用户听。因此,当浏览器的焦点落到<input type="text">时,用户能通过视频阅读器了解到他们正在应对一个文本输入框(可以输入内容的东东)。但如果焦点落到了一个空的&lt;div&gt;上,那么屏幕阅读器不会报道任何信息。

为了解决这个问题,我们可以参考 WAI-ARIA(Web无障碍标准计划 - 无障碍富互联网应用),通过添加特殊的ARIA属性提高组件标签的语义化。这些额外语法能帮助阅读器分辨界面上的属性、关系和状态。ARIA的详细使用说明可以在WAI-ARIA开发实践找到。

键盘支持

我们对键盘支持的目标是可以完全通过键盘使用组件。WAI-ARIA为 许多UI控件定义了行为和键盘交互方式。如果想知道组件应支持什么按键,可在文档中寻找并参考类似组件。例如,多选组件与 组合框类似。

除了支持键盘操作,我们还应主动提示用户如何使用按键操作组件(例如,在应用中提供指引)。

视觉无障碍

这里会介绍组件的视觉无障碍问题。请保证你能对以下问题回答“是”:

  • 所有元素和文字都足够大,能被看清楚吗?
  • 组件在高对比模式下是否能清晰传达信息?
  • 若组件没有颜色,是否能被正常使用?

记住,并不是所有有视力障碍的用户都完全看不见。有相当一部分用户是弱视或色盲。

提高多选Web组件的可访问性

现在,我们将通过前面提到的方法,提高多选组件的可访问性。以下是具体的步骤:

  • 扩展标签语义
  • 添加键盘支持
  • 验证视觉可访问性

别忘了,你可以在文章的底部查看组件示例,或者通过GitHub仓库下载代码。

所有的代码片段都可以通过multiselect.html获取

扩展标签语法

经验表明,我们最好优先使用原生HTML元素,而不是自定义组件。如果你可以使用内置无障碍功能的原生HTML控件,那么就尽量使用它。万不得已时,再自定义组件并使用ARIA属性。在HTML页面中使用WAI-AIRA避免冗余中有更详细的说明。

在这个例子中,多选组件是一个自定义组件,因此,我们需要使用ARIA属性。首先,让我们在ARIA文档中找到类似的组件。貌似多选组件与复合框组件比较相似。那么,根据复合框描述,看看我们需要添加哪些ARIA属性。

根据指引,我们需要添加以下角色:

  1. role="combobox" 组件的根元素
  2. role="listbox" 选项列表
  3. role="option" 下拉列表中的每一项

需要添加的ARIA状态属性:

  1. aria-expanded="true/false" 为根元素添加该状态,以表明组件是打开还是关闭状态
  2. aria-selected="true/false" 为下拉列表的每一项添加该状态,以表明选项是否被选中

我们可以直接将comboboxlistbox角色添加到组件的标签中:

<div class="multiselect" role="combobox">
  <div class="multiselect-field">&lt;/div&gt;
  <div class="multiselect-popup">
    <ul class="multiselect-list" role="listbox">
      <content select="li">&lt;/content&gt;
    &lt;/ul&gt;
  &lt;/div&gt;
&lt;/div&gt;

为了给每个选项添加option角色,我们需要在refreshItems方法中遍历选项。当组件被创建时,这个方法会被调用。

multiselectPrototype.render = function() {
  this.attachHandlers();
  this.refreshField();
  this.refreshItems();
};

multiselectPrototype.refreshItems = function() {
  var itemElements = this.itemElements();

  for(var i = 0; i < itemElements.length; i++) {
    var itemElement = itemElements[i];

    // set role and aria-selected property of an item
    itemElement.setAttribute("role", "option");
    itemElement.setAttribute("aria-selected", itemElement.hasAttribute("selected"));
  }
};

multiselectPrototype.itemElements = function() {
  return this.querySelectorAll("li");
};

我们可以在togglePopup方法中为控件添加aria-expanded属性。该属性决定了显示还是隐藏该列表。

multiselectPrototype.togglePopup = function(show) {
  this._isOpened = show;
  this._popup.style.display = show ? "block" : "none";

  // set aria-expanded property
  this._control.setAttribute("aria-expanded", show);
};

我们还要根据每个选项的selected属性初始化它们的aria-selected属性。aria-selected属性用于表示选项的选中状态。我们可以通过selectItem方法和unselectItem方法更新该状态。

multiselectPrototype.selectItem = function(item) {
  if(!item.hasAttribute("selected")) {
    // set aria-selected property of selected item
    item.setAttribute("aria-selected", true);

    item.setAttribute("selected", "selected");
    this.fireChangeEvent();
    this.refreshField();
  }

  this.close();
};

multiselectPrototype.unselectItem = function(item) {
  // set aria-selected property of unselected item
  item.setAttribute("aria-selected", false);

  item.removeAttribute("selected");
  this.fireChangeEvent();
  this.refreshField();
};

ARIA属性添加好了。下一步是支持键盘操作。

添加键盘支持

打开标准文档,阅读Keyboard Interaction部分,看看我们需要支持哪些操作。

以下是使用键盘操作多选框需要的基本按键:

  1. Alt + Up/Down Arrow – 打开/关闭多选框

  2. Esc – 关闭多选框

  3. Up/Down Arrow – 在选项之间切换

  4. Enter – 在多选框打开时选中某个选项

  5. Backspace – 取消最近一次的选中

使组件可聚焦

支持键盘操作第一步是支持聚焦到组件。我们需要设置tabindex属性来完成这一步,该属性的行为依赖于它的值:

  • 正的属性值定义了聚焦组件的顺序
  • 0 - 该元素获得焦点的顺序由浏览器决定
  • -1 - 用户无法通过键盘选中该元素,但开发者可通过使用Javascript的focus()方法选中它

自定义组件的tabindex应该为-10,因为我们无法知道元素在页面上的获得焦点的顺序。因此,我们将多选组件的tabindex设为了0

<div class="multiselect-field" tabindex="0" aria-readonly="true">&lt;/div&gt;

下一步是处理keydown事件:

multiselectPrototype.attachHandlers = function() {
  this._control.addEventListener("keydown", this.keyDownHandler.bind(this));
  ...
};

keyDownHandler根据event.which,调用特定的按键处理函数。

multiselectPrototype.keyDownHandler = function(event) {
  switch(event.which) {
    case 8:  // Backspace
      this.handleBackspaceKey();
      break;
    case 13: // Enter
      this.handleEnterKey();
      break;
    case 27: // Escape
      this.handleEscapeKey();
      break;
    case 38: // Up Arrow
      event.altKey ? this.handleAltArrowUpKey() : this.handleArrowUpKey();
      break;
    case 40: // Down Arrow
      event.altKey ? this.handleAltArrowDownKey() : this.handleArrowDownKey();
      break;
    default:
      return;
  }

  // prevent native browser key handling
  event.preventDefault();
};

一旦用户按下按键,我们便通过event.preventDefault()阻止默认行为。

使用键盘打开/关闭下拉框

用户可通过Alt + Down Arrow组合按键打开多选框,通过Alt + Up ArrowEsc关闭多选框:

multiselectPrototype.handleAltArrowDownKey = function() {
  this.open();
};

multiselectPrototype.handleAltArrowUpKey = function() {
  this.close();
};

multiselectPrototype.handleEscapeKey = function() {
  this.close();
};

openclose方法会调用togglePopup

multiselectPrototype.open = function() {
  this.togglePopup(true);
};

multiselectPrototype.close = function() {
  this.togglePopup(false);
};

通过键盘浏览选项

首先,将每个选项的tabindex都设为-1,这样我们便可以聚焦到它们。

multiselectPrototype.refreshItems = function() {
  var itemElements = this.itemElements();

  for(var i = 0; i < itemElements.length; i++) {
    var itemElement = itemElements[i];
    ...
    // 设置选项的tabindex属性
    itemElement.setAttribute("tabindex", -1);
  }

  // 初始化聚焦选项的索引
  this._focusedItemIndex = 0;
};

_focusedItemIndex属性保存了聚焦的选项的索引。

用户可通过Up ArrowDown Arrow浏览选项。代码可通过刷新当前聚焦选项索引来实现浏览功能。

multiselectPrototype.handleArrowDownKey = function() {
  this._focusedItemIndex = (this._focusedItemIndex < this.itemElements().length - 1)
      ? this._focusedItemIndex + 1   // go to the next item
      : 0;                           // go to the first item

  this.refreshFocusedItem();
};

当用户在列表底部按下Down Arrow时,阅读器会聚焦到第一个选项。

当用户在列表顶部按下Up Arrow时,阅读器会聚焦到最后一个选项。

multiselectPrototype.handleArrowUpKey = function() {
  this._focusedItemIndex = (this._focusedItemIndex > 0)
    ? this._focusedItemIndex - 1        // go to the previous item
    : this.itemElements().length - 1;   // go to the last item

  this.refreshFocusedItem();
};

refreshFocusedItem方法会聚焦到索引为_focusedItemIndex的选项:

multiselectPrototype.refreshFocusedItem = function() {
  this.itemElements()[this._focusedItemIndex].focus();
};

最后,我们需要修改openclose方法。使得当多选框被打开时,阅读器会自动聚焦到_focusedItemIndex对应的选项,当多选框被关闭时,阅读器会聚焦到多选框容器。

multiselectPrototype.open = function() {
  this.togglePopup(true);
  this.refreshFocusedItem();
};

multiselectPrototype.close = function() {
  this.togglePopup(false);
  this._field.focus();
};

现在我们可通过添加CSS,使得聚焦的选项看起来更突出:

::content li:focus {
  outline: dotted 1px #333;
  background: #efefef;
}

通过键盘选中/取消选中选项

用户可通过Enter按键选中当前聚焦的选项。如果多选框为打开状态,我们可以获得聚焦的选项,并通过selectItem方法选中它。

multiselectPrototype.handleEnterKey = function() {
  if(this._isOpened) {
    var focusedItem = this.itemElements()[this._focusedItemIndex];
    this.selectItem(focusedItem);
  }
};

用户可通过Esc取消最近一次的选择。如果当前有被选中的选项,我们可以通过unselectItem方法取消最近一次的选择。

multiselectPrototype.handleBackspaceKey = function() {
  var selectedItemElements = this.querySelectorAll("li[selected]");

  if(selectedItemElements.length) {
    this.unselectItem(selectedItemElements[selectedItemElements.length - 1]);
  }
};

现在我们支持了所有必要的键盘操作,因此用户可完全通过键盘操作组件。

视觉无障碍

组件尺寸

多选框使用了相对单位em,它的大小依赖于容器的字体大小。因此,组件的字体大小是灵活的,如果有必要,我们可以轻松的增大字体。

多选框的不同大小

高对比度模式

弱视或有其它视觉障碍的用户有时会使用高对比度模式。OSX系统的用户可在设置中启用高对比度模式,Windows系统也提供了高对比度主题。除此之外,Chrome的扩展 High Contrast也支持用户在浏览器中使用高对比度颜色。

一起来看看组件在高对比度模式的样子:

高对比度模式中的多选框

它看着还行,但是有一个问题:被选中的选项与其它选项看着区别不大。因此,我们可以稍微调整被选中的选项的颜色,来解决这个问题。

高对比度模式中的多选框 - 优化版本

没有颜色

颜色无障碍是视觉无障碍的一个重要方面。互联网圈子中有许多用户是色盲。因此,我们不应完全依赖于颜色来传达重要信息。我们可以在灰度模式中检查组件(通常操作系统或某些软件都会提供这个功能)。由于多选组件本身就是灰色的,因此我们可以跳过这一步。

示例

经过以上的优化,这里是最终的代码:

Multiselect Web Component by SitePoint (@SitePoint) on CodePen.

总结

为了提高组件的可访问性,我们应该确保一下几点:

  • 标签语义化,这样屏幕阅读器等辅助工具才能帮助用户使用组件。尽量使用原生的HTML控件,如果必须自定义控件,请添加有意义的ARIA属性。

  • 可以完全通过键盘操作组件。通过tabindex使组件可被聚焦。参考ARIA实践指南实现键盘交互。

  • 组件在高对比度模式和无色模式下也要能正常显示。

感谢你的阅读。欢迎大家评论,分享在web组件可访问性方面的经验。

标签: accessibility, keyboard accessibility, multiselect component, polymer, web components, X-Tag

英文原文:https://www.sitepoint.com/accessible-web-components/?utm_source=html5weekly&utm_medium=email

本文链接:https://75team.com/post/how-to-make-accessible-web-components-—-a-brief-guide.html

-- EOF --

Comments

评论加载中...

注:如果长时间无法加载,请针对 disq.us | disquscdn.com | disqus.com 启用代理。