本文由Mallory van Achterberg进行同行评审。 感谢所有SitePoint的同行评审人员使SitePoint内容达到最佳状态!
在上一篇文章中,我演示了如何创建多选Web组件。 在该文章的评论部分,读者谈到了Web组件可访问性这一非常重要的主题。 毫无疑问,对于当今的Web来说,可访问性至关重要,因此让我们讨论可访问性代表什么,并(通过一个实际示例)了解如何使Web组件更易于访问。
本文中的代码将基于上一篇文章中的代码。 您可以从我们的GitHub存储库中获取它的副本,或者在本文结尾查看可访问组件的演示 。
Web组件可访问性需要什么?
在谈论组件的可访问性时,我们通常考虑以下方面:
- 标记语义
- 键盘支持
- 视觉可及性
让我们更详细地讨论每个方面。
标记语义
我确定您听说过屏幕阅读器。 屏幕阅读器是一种辅助软件,它允许盲人或视障人士通过大声阅读屏幕上显示的信息来使用应用程序。 那里有许多屏幕阅读器,其中包括适用于Windows的NVDA和JAWS ,适用于Chrome的ChromeVox和适用于OS X的VoiceOver 。
当元素获得焦点时,屏幕阅读器会向用户提供有关它的信息。 因此,当聚焦HTML <input type="text">
,用户从屏幕阅读器知道他们正在处理文本字段(并且可以输入一些内容)。 但是,如果该元素只是一个简单的<div>
,则屏幕阅读器对此无话可说。
为了解决此问题,我们可以使用WAI-ARIA (Web可访问性计划–可访问的富Internet应用程序)添加特殊的ARIA属性以扩展组件标记的语义。 这些附加的语义帮助辅助技术在用户界面中标识属性,关系和状态。 可以在以下位置找到使用ARIA的实用指南: WAI-ARIA创作实践 ,或者,(为了快速复习)您可以阅读我们的WAI-ARIA简介 。
键盘支持
目的是使仅使用键盘即可与组件进行交互。 WAI-ARIA定义了许多UI控件的行为和键盘交互。 要知道哪个组件应该支持哪些键,请在说明中找到您的组件或类似组件的描述,然后使用它。 例如,多重选择类似于combobox 。
即使可以使用键盘,也应该让用户知道使用哪些键/组合键与组件进行交互(例如,通过在应用程序中提供一些说明),因为这可能并不明显。
视觉辅助功能
在这里,我们正在讨论与组件外观相关的可访问性方面。 确保您对以下问题回答“是”:
- 元素和文本足够大以清楚地看到它们吗?
- 在高对比度模式下,您的组件看起来是否像预期的那样?
- 是否可以使用没有颜色的组件?
请记住,并非所有视力障碍的使用者在法律上都是盲人 。 有很多用户(例如)视力低下或色盲。
使Multiselect Web组件可访问
现在,我们将使用上面概述的所有技术使多重选择更加易于访问。 具体来说,我们将要:
- 扩展标记语义
- 添加键盘支持
- 验证其视觉可访问性
别忘了,您可以在文章结尾查看该组件的演示,或从我们的GitHub repo下载代码。
所有代码段都可以在multiselect.html文件中找到。
扩展标记语义
经验的可访问性规则是在自定义元素上使用本机HTML元素。 这意味着,如果您可以使用具有内置可访问性的本机HTML控件,请这样做。 仅在确实需要创建自定义组件时才添加ARIA属性。 如果您想了解更多信息,请阅读HTML Pages中的“通过WAI-ARIA避免冗余” 。
在我们的例子中,multiselect是一个自定义组件,因此我们需要添加ARIA属性。 首先,让我们找到类似于ARIA规范中的multiselect的组件。 经过一些研究,组合框看起来和行为类似。 太好了,现在让我们看看需要根据组合框描述添加哪些ARIA属性。
从准则中我们可以看到我们需要添加以下角色:
-
role="combobox"
到组件的根元素 -
role="listbox"
到弹出窗口中的项目列表 - 下拉列表中每个项目的
role="option"
要添加的aria状态属性:
-
aria-expanded="true/false"
指向根元素,以指示该组件是打开还是关闭 -
aria-selected="true/false"
到下拉列表的每个项目以指示所选状态
角色combobox
和listbox
可以直接添加到组件的标记中:
<div class="multiselect" role="combobox">
<div class="multiselect-field"></div>
<div class="multiselect-popup">
<ul class="multiselect-list" role="listbox">
<content select="li"></content>
</ul>
</div>
</div>
为了将角色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');
};
可以将aria-expanded
属性添加到togglePopup
方法中的控件中(顾名思义),该方法负责显示和隐藏弹出窗口:
multiselectPrototype.togglePopup = function(show) {
this._isOpened = show;
this._popup.style.display = show ? 'block' : 'none';
// set aria-expanded property
this._control.setAttribute("aria-expanded", show);
};
我们还根据项目aria-selected
属性来初始化其aria-selected
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属性。 下一步是键盘支持。
添加键盘支持
让我们打开规格并查看“ 键盘交互”部分,以了解我们需要支持哪些交互。
这是仅可通过键盘使用多重选择的基本键集:
-
Alt + Up/Down Arrow
–打开/关闭多选 -
Esc
–关闭多选 -
Up/Down Arrow
–浏览项目 -
Enter
–打开多重选择时选择一个项目 -
Backspace
–取消选择最后一个选定的项目
使其聚焦
增加键盘支持的第一步是使组件具有焦点。 为此,我们需要设置tabindex属性,该属性的行为取决于tabindex
值:
- 正整数-定义键盘焦点导航中元素的顺序
-
0
–键盘焦点导航中元素的顺序由浏览器定义 -
-1
–键盘焦点导航无法到达该元素,但现在可以使用JavaScript的focus()
方法以编程方式接收焦点。
对于自定义组件, tabindex
应该为-1
或0
,因为我们无法知道目标页面上元素的顺序。 因此,我们直接在标记中将multiselect字段上的tabindex
设置为0
:
<div class="multiselect-field" tabindex="0" aria-readonly="true"></div>
下一步是处理keydown
上的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 Arrow
和Esc
键应将其关闭:
multiselectPrototype.handleAltArrowDownKey = function() {
this.open();
};
multiselectPrototype.handleAltArrowUpKey = function() {
this.close();
};
multiselectPrototype.handleEscapeKey = function() {
this.close();
};
open
和close
方法只需调用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];
...
// set item tabindex attribute
itemElement.setAttribute("tabindex", -1);
}
// initialize focused item index
this._focusedItemIndex = 0;
};
_focusedItemIndex
属性存储焦点项目的索引。
Up Arrow
和Down 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();
};
最后,我们需要更改open
和close
方法,以便在打开_focusedItemIndex
,焦点移到索引为_focusedItemIndex
的项目,而在控件关闭时,焦点移回到multiselect字段:
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
具有相对大小,其大小取决于容器的字体大小。 因此,它具有可伸缩性,并且可以根据需要轻松地增加:
高对比度模式
视力低下或其他视力障碍的人有时会使用高对比度模式。 OS X允许用户在设置中启用高对比度,而Windows提供了特殊的“ 高对比度主题” 。 还有一个流行的Chrome扩展程序(令人惊讶地)是High Contrast ,它使用户可以使用高对比度滤色镜浏览网络。
让我们看看我们的组件在高对比度模式下的外观:
看起来还可以,但是有一个问题:选定的项目与未选定的项目很难区分。 选定和重点突出的项目的颜色略有变化可解决此问题:
没有颜色
颜色可访问性是视觉可访问性的另一个重要方面。 有许多色盲用户正在浏览网络。 这意味着颜色不应该是向用户传达重要信息的唯一方法。 在这里,我们可以检查组件在灰度模式下的外观(通常由OS或特殊应用程序提供)。 如此说来,我们的组件仅由灰色组成,因此这是我们可以跳过的检查。
演示版
完成上述所有操作后,这便是最终产品:
请参阅CodePen上的SitePoint ( @SitePoint )的Pen Multiselect Web组件 。
结论
为了使Web组件更易于访问,我们需要确保:
- 标记是语义的,因此诸如屏幕阅读器之类的辅助技术可以在用户与组件交互时为他们提供帮助。 为此,请尝试使用本机HTML控件,或者在使用自定义控件的情况下,添加有意义的ARIA属性。
- 该组件只能与键盘一起使用。 为了实现这一点,可以使用
tabindex
使组件具有焦点。 遵循ARIA实用指南实现键盘交互。 - 该组件可以在高对比度模式下使用,并且没有颜色。
谢谢阅读。 随时分享您在评论中访问Web组件的经验。