自定义键盘组件_WEB组件终极开发手册:让玩家没了鼠标也能操作

5c6ab765d6e089a47ab8fbc3367cebe7.png

在往期的一篇文章中(Accessibility:为所有人而设计),我们介绍了Accessibility是什么,并通过一些示例说明了如何将Accessibility做好,其重点放在了介绍Web页面的视觉设计上。本篇文章将从技术实现的角度,讲解如何开发具有完善的Accessibility功能的Web组件。

面向Accessibility,我们的Web组件需要满足两个条件:

  1. 用户能够仅用键盘完成通过鼠标能进行的所有操作;
  2. 用户能够在闭上眼睛的情况下完成在睁开眼睛时能完成的所有操作;

为了满足第一个条件,Web组件需要能够响应tab、方向、空格和回车等按键事件,并且表现出与鼠标事件等价的响应行为。我们把这一能力姑且称为“键盘响应力”。为了满足第二个条件,Web组件需要提供能够被系统发声平台识别的内容元素,通俗的讲,即要使得系统能够读出有用的Web组件信息,供闭上眼睛的用户参考:)。我们把这一能力姑且称为“讲述力”。

接下来,我们详细了解一下,如何让我们的Web组件具有这两种神奇的能力。


键盘响应力

最基本的,我们需要支持通过tab键来切换被聚焦(focus)的Web组件:

e570f05c886743b46718f8dde37c5efc.png
1865158c6a9b431ccb645bebdc0dc819.gif

上面的动图记录了用户在页面上持续按tab键时,各个元素被聚焦的情况。像, 和等等这些基本html元素,默认地支持被tab键聚焦。而对于

,
这类元素,默认是不能被tab聚焦上的。

我们可以为元素加上tabindex属性,使得不能默认被聚焦的元素能够被聚焦:

e5fa6b8720c162c4d8a349bc842e9ef5.png
2d04d3b55907473d526289e6b4162f32.gif

从上面的动图可见,给

元素加上tabindex=”0”之后,他们也可以获得聚焦了。同时也能够指定元素之间被聚焦的顺序,通过设置tabindex的值可以做到这一点:“-1”值表示元素永远不会获得聚焦,“0”表示按照源码中定义的顺序,从“1”开始,数字越大,顺位越低。我们在开发自定义组件时,较多地使用了“-1”和“0”。前者是为了排除掉不希望被聚焦的元素(例如一个 里面包含了一个,我们只希望外面的这个获得聚焦而忽略里面的,否则,用户连按两次tab键,从视觉上看,聚焦依然停留在这个元素上。而期望的行为是,焦点移到下一个视觉元素上)。后者就是很常规的用法,强行给元素加上获取聚焦的能力。

上述通过tabindex安排元素的聚焦行为,只是一切的开始。由于我们的组件有明确的UX(User Experience,用户体验)设计,我们还需要为被聚焦的元素定义特定的样式。需求是:当且仅当元素因tab键被按下而获得焦点时,为元素加上特定的样式。意味着,这种样式,还不能在被鼠标点击获得焦点时就出现。有个pseudo-class(伪类)叫focus-visible可以帮助我们实现这一需求:

e7eb1100a6395ccb0dc60b5881ad8fd6.png

来看一看效果吧:

d6bf570af2ecaf3b2b0f68c336369a6c.gif

不过,使用focus-visible有一个很大的问题:它还是一个处于试验阶段的伪类,除了Firefox的某些版本以外,目前其他浏览器都不支持(Chrome可以通过打开对应的flag得到支持)。基于这一事实,我们有三个替换方案:

  1. 使用开源的focus polyfill模拟focus-visible的native实现;
  2. 自己用js写实现;
  3. 让UX同学改设计,允许鼠标focus上的时候也呈现一样的样式。

对于2,我们开发周期十分有限,所以先不考虑。对于3,看了下UX同学坚定的眼神,也默默放弃。于是,我们从开源社区挑选了一个靠谱的polyfill,经过多次测试和调试,将它应用到了我们最终的产品中。

到目前为止一切顺利。还剩一个问题需要解决:我们有若干全自主产权,从零搭起来的基础组件,最具代表性的是以下两位下拉列表老铁:

a704fd0c1c77115ea1ef82cef693831f.gif

Dropdown

DropInput

e7f346c886c0ff3d20efaec8c2be84b1.gif

由于Accessibility是在项目后期才突然加进的需求,开发哥哥早期在写这些自定义组件代码时,压根不用面向Accessibility,因此全部用了默认无法被tab聚焦的元素来组装成最终的成品。由于进度紧迫,我们不可能冒着破坏功能的风险重构这些已经经过测试的组件,所以,只能硬着头皮往上加面向Accessibility的行为了。需要搞定两个问题:

  • 如何实现用tab聚焦;
  • 如何用键盘与这些组件交互。

解决第一个问题的方案是很直观的,如前文所说,往该聚焦的元素上加上tabindex=”0”即可。第二个问题有些复杂:当下拉列表的当前选项被聚焦时,按向下键,弹出下拉列表,同时焦点移到下拉列表中的第一个选项。从这一刻开始,通过键盘向上向下键可以依次地在选项之间循环聚焦,直到Esc键被按下或者空格键/回车键被按下,下拉列表才会收起,且如果是空格键/回车键被按下,下拉列表收起前被聚焦的选项会被选中;如果是Esc键被按下,则不会改变初始被选中的选项。我们的实现分为三步:

  1. 定义两个方法:handleArrowKeyUpDown和handleEscKeyDown。前者被同时绑定到下拉列表的当前选项和所有选项的keyDown属性上,后者只被绑定到所有选项的keyDown属性上;
  2. 为每一个下拉列表加上ref(即React里面的ref),并将这些ref存到一个全局的数组中;并且定义全局变量currentFocusedIndex,用于存储当前被聚焦的选项的ref在数组中的位置,-1表示当前没有选项被聚焦;
  3. 定义以下方法:accessibilityOnSpaceAndEnterKeyDown;它只有两个参数:键盘事件e和一个函数func。意思是当e是回车键或空格键时,func将被执行。它被绑定在每一个选项的keyDown属性上;

有了这三步,水到渠成:当通过tab键聚焦到下拉列表的当前选项,此时如果向下键被按下,handleArrowKeyUpDown会被调用(参见1),该方法会看currentFocusIndex的值,如果是-1,就会把currentFocusIndex置成0,则下一次React重新渲染组件时,我们的代码就会把第0个ref进行聚焦(参见2);接下来,不用我赘述,相信大家都能想到,如果此时currentFocusIndex是一个非-1的值,handleArrowKeyUpDown会对currentFocusIndex值进行自增操作(如果超过了数组长度,则重置为0,从而实现循环聚焦各个选项)。

对于向上按键的处理,逻辑相同,只不过上述的自增操作变成了自减。另一方面,在任何一个选项被聚焦时按下回车键或者空格键,都会导致accessibilityOnSpaceAndEnterKeyDown被调用,并传入之前开发哥哥早已写好的选项被鼠标点击时被调用的函数(更改下拉列表的被选中的值)作为参数,就实现了通过回车键/空格键选中选项的功能。handleEscKeyDown的实现就更加直观了,每当Esc键被按下,隐藏掉下拉列表。

让我们直观的感受一下效果:

de5547810d12b45a17f36c115689d01b.gif

至此,我们成功地将“键盘响应力”赋予了所有组件。


讲述力

接下来我们为组件赋予“讲述力”,即操作系统能够将我们页面中的内容读出来。听起来似乎这是操作系统的事,但实际上,工作量在我们这边:需要给组件加上一些属性甚至是逻辑,从而操作系统才能知道,它要读什么。

假如有如下一个button:

0638a49763bf67bd4881a08418da6250.png

操作系统的屏幕阅读器(例如MacOS中的VoiceOver,Windows中的Narrator)见到这个button,会响亮地朗读出“埃克斯”(X)……

为了让阅读器能够读出对用户而言有意义的内容,我们对这个button做一点小小的修饰:

d9dff927f9b411a90d61dec29587a640.png

为它加上了aria-label属性。如此一来,当用户通过tab键聚焦到这个button上时,屏幕阅读器就会把aria-label的值读出来。这时,用户就知道如果想要关闭当前窗口的话,按一下空格键就可以了。

对于一些存在一定交互性的组件,例如togglebutton:

0291b82b38929a7f4826e60e9e63ba6c.png

我们还希望屏幕阅读器能够读出它最新的状态,可以通过为组件设置aria-live属性来完成,有了它,一旦组件的状态发生变化(内容、属性、样式等的改变)屏幕阅读器便会再读一遍aria-label中的内容。我们便可以根据组件的状态设置不同的aria-label的值,从而达到让组件的状态变化被屏幕阅读器读出来的效果:

50d273053e82a951e1c8bf46f3baf688.gif

至此,我们具有“键盘响应力”和“讲述力”的组件就完成了。


最后

随着国内外法律法规的不断完善,软件产品的Accessibility越来越成为“刚需”。笔者在这里建议大家将Accessibility加入到产品需求中,并且在设计、开发和测试等各个阶段充分考虑Accessibility,让自己的产品能够被更多的人在更广泛的场景下使用。

我们会每周推送商业智能、数据分析资讯、技术干货和程序员日常生活,欢迎关注我们的头条&知乎公众号“微策略中国”或微信公众号“微策略 商业智能"。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值