在Backbone/Angular单页面应用中,告诉屏幕阅读器页面已经发生改变

14

假设你有一个简单的单页应用程序,无论是使用Backbone、Angular、Ember还是其他任何工具编写的。

当我们跟随路由进行页面跳转时,如何告知屏幕阅读器已经更改了"页面(page)"?

在传统的应用程序中,当我从/index.html导航到/about.html时,屏幕阅读器显然会检测到页面的变化,并按照预期重新阅读。

但是,在我的Backbone应用程序中,当我跟随某个路由时,我无法弄清楚如何触发'重新阅读(re-read)'。我已经尝试触发focus事件,但似乎没有效果。

注意: 我目前正在使用NVDA/Chrome进行测试。


你是否正在使用HTML5模式?禁用它可能会解决问题。 - sushain97
这是使用pushState而不是hash片段的Angular模式吗?如果是,我已经尝试过有和没有,但都没有成功(使用带有pushState true和false的Backbone)。 - isNaN1247
我有一个未完成的任务需要自己去研究,所以我很感兴趣你可能会发现什么。你是否已经研究过动态分配其中一个aria-roles:http://www.w3.org/TR/wai-aria/states_and_properties#attrs_liveregions - kinakuta
另外请查看http://www.w3.org/TR/wai-aria/states_and_properties#aria-live。 - TheSharpieOne
2
非常小心使用活动区域,它们旨在用于一般内容,而是用于小段文本,以通知用户更新,例如您会放在“黄色淡出”样式警报框中的内容。 - AlastairC
显示剩余3条评论
3个回答

19

总的来说,你不应该需要触发“重新阅读”,而且根据你的用户界面,这可能也不是一个好的选择。

我的经验是使用angular.js(作为一个辅助功能人员而不是开发人员),我们的整体方法是管理焦点而不是触发重新阅读。(我们进行了广泛的辅助功能测试。)

从用户界面的角度来看(主要针对屏幕阅读器用户),选择链接(例如about.html)应该可以带你到某个地方。

在这种情况下,将焦点放置在关于“页面”的内容区域顶部,希望能够实现<h1>标签的标题。

为了使其正常工作,目标元素应该通过脚本具有可聚焦性,因此可能需要使用tabindex,除非它是链接或表单控件:

<h1 id="test" tabindex="-1">

-1 表示元素 不在 默认的 Tab 键顺序中,但可以通过脚本聚焦。更多信息请查看WAI-ARIA 作者实践

然后,在加载新内容的函数末尾,包括 tabindex 属性,添加类似以下的代码:

$('#test').attr('tabindex', '-1').css('outline', 'none');
$('#test').focus();

动态添加 tabindex 时最好在调用 focus() 函数之前的同一行添加,否则可能无法正常工作(我记得在测试 JAWS 时遇到了这个问题)。

测试时可以使用以下任一组合:

  • NVDA 和 Firefox,在 Windows 系统上
  • Jaws 和 IE,在 Windows 系统上

在 Safari/OSX 上使用 VoiceOver 进行测试也很好,但它的工作方式有所不同,并且可能不会遇到与基于 Windows 的屏幕阅读器相同的问题。

如果使用 Chrome/NVDA,您将会遇到许多问题,因为它的支持并不是非常好,终端用户也十分不可能使用它。IE 与 NVDA 配合使用还可以,但Firefox 是最好的选择

总的来说,值得了解 WAI-ARIA 作者实践指南,特别是在 HTML 中使用 ARIA。即使您使用的是 JS 应用程序,浏览器(以及屏幕阅读器)仍然会解释生成的 HTML,因此该建议仍然适用。

最后,如果您没有要求用户按下空格/回车键来激活某些操作而更新页面内容,您可能会发现 JAWS/NVDA 不知道新内容的存在,因为它们的“虚拟缓冲区”没有更新。在这种情况下,您可能需要添加一个JS shim 来刷新它们,但只有在测试中遇到问题时才这样做,而不要进行全局修补。


我刚开始写一个类似的文章,所以感谢您节省了时间!在尝试了NVDA和VoiceOver中的各种解决方案后,我得出了相同的结论。在我的backbone应用程序中,现在有一个处理程序,每次触发路由时都会被触发,该处理程序找到第一个标题-将tabindex设置为-1并进行聚焦:$(':header:first').attr('tabindex', '-1').focus(); - isNaN1247
稍作修改的代码,用于隐藏标题的轮廓线:$(':header:first').attr('tabindex', '-1').css('outline', 'none').focus(); - isNaN1247
太棒了。不过要注意,在同一行中包括 tabindexfocus,我们发现除非在 focus 之前的行中分配了 tabindex,否则无法正常工作。 - AlastairC
啊,是的,我应该提到我也尝试过将焦点包装在一个1毫秒的setTimeout中,以确保它总是在“下一个刻度”上发生。 - isNaN1247

8

参考@AlastairC及其下面的评论,我对此进行了更深入的研究,并将其作为我的解决方案:


我的解决方案

我发现仅仅读取第一个标题并不是很有用,特别是当你上一页正在加载时。你可以听到有新的焦点出现,但这显然不能清楚地说明它是整个页面的一部分。

在页面中添加一些有用的、描述性的文本

因此,我现在在我的页面布局模板顶部有一个单独的段落。其中包括一个面向屏幕阅读器的友好信息,以及对页面的非常粗略的概述。

<p class="screenreader-summary" tabindex="-1">
  The <strong>Dashboard</strong> page is now on-screen.
  It contains several widgets for summarizing your data.
</p>

请注意,tabindex值使我们可以使用JavaScript聚焦此元素。您可能不想使用p元素进行此操作,实际上您可以使用任何元素。

将其隐藏在屏幕外(可选项,仅在它会破坏您的设计/可读性时需要)

然后与CSS配合使用,将该元素移出屏幕:

.screenreader-summary {
  position: absolute;
  left:-10000px;
  top:auto;
  width:1px;
  height:1px;
  overflow:hidden;
  outline: none; /* Important, don't show an outline on-focus */
}

当屏幕上显示新页面时,将焦点放在此元素上

最后,在JavaScript代码中显示您的页面(例如,在使用onShowshow事件的MarionetteJS中):

$yourViewEl.find('.screenreader-summary').focus();

结果

我是一个有视力的人,所以请抱着一颗谨慎的心来听我的话 - 然而我发现朗读一个简短的描述会更加有用。


注:本文中的HTML标签已保留。

-3

对于Angular而言,URL会发生变化。如果有人真的需要重新阅读所有内容(比如我因为要求),我们使用以下方法解决了这个问题:

$(window).on('hashchange', function () {
    location.reload();
});

我们刚刚添加了额外的代码来处理像“成功”这样的页面,不应该发生重新加载。 这模拟了实际页面加载,屏幕阅读器将像页面更改一样正常阅读它。 在某种程度上有点违背了Angular的目的,但这将帮助像我这样已经拥有应用程序并想要快速修复的人。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接