Heim >Web-Frontend >HTML-Tutorial >【译】建立更好的可访问性原语_html/css_WEB-ITnose
原文: http://www.zcfy.cc/article/607
随着我们建立的网站逐渐变得更像 app,web 平台也需要跟上,提供给开发者所需的创建高可访问性用户体验的工具,这一点很重要。
最近,我遇到两个场景,在这两个场景下我添加合适的键盘支持到我所创建的组件上是极其困难的。在经过很多实验和研究之后,我比较清晰地意识到可能在 web 平台上缺少了一些原语,如果有这些原语,我的工作将会变得简单一些。我将会解释这两个场景,并涵盖一些关于如何解决这两个问题的思路。
模态窗口是一个出现在页面上的对话框,通常用一个遮罩层遮住它背后的内容。如果一个模态窗口正在被展现,用户不应该能操作网页上的任何其他东西。
如果你曾经试图使模态窗口具备好的可访问性,那你应该知道即使它们看起来很无害, 模态窗口实际上是终结 web 可访问性的大 boss 。它们会把你吃掉,吐出骨头来。例如,一个合适的模态窗将需要有以下特点:
键盘焦点应该要被移动到模态窗口中,并且当窗口关闭时恢复到之前获得焦点的元素。
键盘焦点应该被限制在模态窗口中,从而用户不会意外敲 tab 键把焦点移出模态窗口。
读屏软件应该也被限制在模态窗口中,以避免意外离开模态窗口。
做到以上三点可以说是相当困难的,需要大量的 aria-hidden 组合变换来限制读屏软件,并且还需要管理焦点切换来限制 TAB 。 这里有一个我能找到的最好的例子 。请注意这个例子假设你的模态窗口是与你的 main content 同级的 dom,这样你可以简单将 aria-hidden 应用于 main 元素。然而,如果你的模态窗口混合在你的 main content 中(可能是因为定位的原因),那么真的有点难搞了,你需要想一个办法将 aria-hidden 应用到 main content 中的每个元素,除了你的模态窗口(或者它的父容器)。这肯定比预想的要难。
HTML 规范 提出了使用
侧边栏菜单是一个在响应式网站非常流行的 UI 模式。但是是否侧边栏菜单与
与其用扭曲
设想的 API 看起来如下:
// put element at the top of the blocking elements stackdocument.blockingElements.push(element); document.blockingElements.pop(); // see https://github.com/whatwg/html/issues/897#issuecomment-198565716document.blockingElements.remove(element); document.blockingElements.top; // or .current or .peek()
将一个元素放到 blockingElements 堆栈的顶部实际上意味着页面上的其他一切被无效化(因此没有了键盘焦点和屏幕阅读器离开模态窗口的风险)。而当一个元素被从堆栈中 pop 掉,焦点自然地回到前一个被聚焦的元素上。这使我们能够解释
目前 blockingElements 仍然是一个新的想法,而新想法是脆弱的,因此请克制在 GitHub issue 里吐槽、打口水仗的冲动 :blush:。我们的下一步是将 blockingELements 移交到 Web Platform Incubator Community Group(WICG) ,从而我们可以继续细化这个想法。当我们移交到 WICG,我一定会更新这篇博客文章及时告诉大家!
让我们再看一眼刚才的侧边栏菜单。为了让这个菜单以动画方式从左侧出现,并达到 60fps 的帧率,我需要让这个菜单有单独的 layer,这可以通过设置 will-change: transform 之类的 css 属性做到。现在我可以用 transform 让菜单出现在屏幕上,并且我没有触发不必要的绘制或布局。这个视频很好地解释了这项技术: Paul Lewis in his I/O presentation.
一个问题:为了做到这个我们必须让这菜单始终处于 DOM 树中。这意味着它可获得焦点的子元素存在于屏幕可见区域外,于是如果用户在页面上不断用 tab 键切换,最后焦点会消失,因为不可见元素获得了焦点,用户不知道它在哪里。我 总是 在响应式网站上看到这种情况。这只是其中一个例子,而当我通过 opacity: 0 来用动画切换元素的显示隐藏,或者暂时禁止一个大列表的自定义控制时,我都需要禁止 tabindex。而正如其他人所指出的,如果你尝试创建类似 coverflow 的交互,你会想要撞墙,因为这种 UI 形式要求你能预览下一个元素但不能直接与它交互。
使用 aria-hidden 有可能将元素和它所有的子元素从可访问性树树(accessibility tree)上移除。这真棒
它解决了读屏软件的问题 ( Marcy Sutton 指出 它并没有完全解决问题,而甚至会导致“ghost controls”,它会获得焦点但是缺失可访问性信息 )。不幸地是,ARIA 被禁止影响页面行为,它只能处理语义,而 tabindex 是和语义完全不同的概念。但 tabindex 很重要,因为有视力障碍的用户要依赖 tab 键来操纵网页。如果你有一个复杂的交互元素树,而你需要将它们全部从 tab order 中移除,你唯一的选择是递归遍历这棵树(或写 一个漂亮的令人发指的 querySelectorAll 字符串 )并给每一个可获得焦点的元素设置 tabindex="-1" 。你也需要记住它们可能已经拥有的任何显式的 tabindex值,以便于当它们重新回到屏幕可见区域时恢复这些值。再一次,这比预想的要难。
设置 pointer-events: none 会让你不能点击一个元素,而它不会将这个元素从 tab order 中移除,所以 你依然可以通过键盘与它交互 。
当侧边栏菜单不可见,我们可以将它设置成 display: none 或者 visibility: hidden 。这会让可获得焦点的子元素从 tab order 中移除。不幸地是,这也会破坏掉我们的 GPU layer,这将导致我们的为动画所做的优化全部失效。当一个菜单足够简单的时候,我们可以不管用户再次打开菜单时重建 layer 的开销,但是如果菜单更复杂(比如像这种: m.facebook.com ),所有的这些重绘将可能导致我们的动画闪动。我不认为开发者在 60fps 动画和好的可访问性之间只能二选一。
早先这里 有一些讨论 是关于增加一个 inert 属性到 HTML。基本思路是你可以设置 inert 属性到一个元素上,然后它和它的后代元素将全部变成不可交互的。这个方案对于我们的侧边栏菜单来说是完美的,因为它意味着我们可以绘制一些屏幕不可见的元素,设置它们的 inert ,而不用担心一个用户通过键盘意外与它交互了。
不幸地是, inert 的原始的使用案例似乎主要是围绕着 dialog 建立的,而由于此刻
我的感觉是弃用 inert 也许是在倒退。我认为被提出的 blockingElements API 是一个让开发者更好地创建可访问的对话框的方法,因为它表示“除了我之外别的元素都是惰性(inert)的”。反之, inert="" 用在上面的动画例子里,当我需要绘制一些东西并插入到 DOM 里面但不希望它可交互(换句话说,“它是惰性的”)时也很有用。而对于从事增强可访问性工作的人来说,它如同一个更强大的 aria-hidden ,因为它不仅仅从 tab order 里移除元素,也从可访问性树里移除元素。 开发者将使用最好的 API 去构建他们的组件,而他们可以自由地获得好的可访问性 。ZOMG 如此完美的胜利!
我应该补充说,其他人在 讨论话题 里提到可能用一个新的 CSS 属性(即 inert——译者注)是更好的解决方法。我也觉得这样很酷,但是以我的(非常有限的)知识没法提出建议,而 这是一个开发者真实的痛点 。所以我报告了 一个 chromium bug 来看是否 Chrome 团队会尝试重启实现 inert 的进程。我希望通过写下这些使用案例,我们可以说服其他人也认同它的作用。如果有任何进展,我一定会写一篇后续文章来让你们知道 :bee:
英文原文: http://robdodson.me/building-better-accessibility-primitives/