简体   繁体   English

使用 aria-live 向 vue.js 中的屏幕阅读器宣布信息

[英]Announcing information to a screen reader in vue.js using aria-live

I am trying to get a vue component to announce information dynamically to a screen reader when different events occur on my site.我试图让一个 vue 组件在我的网站上发生不同的事件时动态地向屏幕阅读器宣布信息。

I have it working to where clicking a button will populate a span that is aria-live="assertive" and role="alert" with text.我让它工作到点击一个按钮将填充一个带有文本的aria-live="assertive"role="alert"跨度。 This works decently the first time, however, clicking other buttons with similar behavior causes NVDA to read the previous text twice before reading the new text.这在第一次运行正常,但是,单击具有类似行为的其他按钮会导致 NVDA 在阅读新文本之前阅读前一个文本两次。 This seems to be happening in vue, but not with a similar setup using jquery, so I'm guessing it has something to do with the way vue renders to the DOM.这似乎发生在 vue 中,但不是使用 jquery 进行类似设置,所以我猜这与 vue 呈现给 DOM 的方式有关。

I'm hoping there is some way to workaround this problem or perhaps a better way to read the text to the user that would not have this issue.我希望有一些方法可以解决这个问题,或者可能有更好的方法来向没有这个问题的用户阅读文本。 Any help is greatly appreciated.任何帮助是极大的赞赏。

Here is a simple component I set up in a working code sandbox to show the problem I am having (navigate to components/HelloWorld.vue for the code) -- Note: This sandbox has changed per the answer below .这是我在工作代码沙箱中设置的一个简单组件,用于显示我遇到的问题(导航到 components/HelloWorld.vue 获取代码)——注意:此沙箱已根据以下答案进行了更改 Full code for the component is below:该组件的完整代码如下:

 export default { name: "HelloWorld", data() { return { ariaText: "" }; }, methods: { button1() { this.ariaText = "This is a bunch of cool text to read to screen readers."; }, button2() { this.ariaText = "This is more cool text to read to screen readers."; }, button3() { this.ariaText = "This text is not cool."; } } };
 <template> <div> <button @click="button1">1</button> <button @click="button2">2</button> <button @click="button3">3</button><br/> <span role="alert" aria-live="assertive">{{ariaText}}</span> </div> </template>

Ok so what I've found works way more consistently is instead of replacing the text in the element with new text, to add a new element to a parent container with the new text to be read.好的,所以我发现更一致的工作方式是,不是用新文本替换元素中的文本,而是将新元素添加到具有要读取的新文本的父容器中。 Instead of storing the text as a single string, I am storing it in an array of strings which will v-for onto the page within an aria-live container.我没有将文本存储为单个字符串,而是将其存储在一个字符串数组中,该数组将v-for显示在aria-live容器内的页面上。

I have built a full component that will do this in various ways as an example for anyone looking to do the same:我已经构建了一个完整的组件,它将以各种方式执行此操作,作为任何希望执行此操作的人的示例:

 export default { props: { value: String, ariaLive: { type: String, default: "assertive", validator: value => { return ['assertive', 'polite', 'off'].indexOf(value) !== -1; } } }, data() { return { textToRead: [] } }, methods: { say(text) { if(text) { this.textToRead.push(text); } } }, mounted() { this.say(this.value); }, watch: { value(val) { this.say(val); } } }
 .assistive-text { position: absolute; margin: -1px; border: 0; padding: 0; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
 <template> <div class="assistive-text" :aria-live="ariaLive" aria-relevant="additions"> <slot></slot> <div v-for="(text, index) in textToRead" :key="index">{{text}}</div> </div> </template>

This can be used by setting a variable on the parent to the v-model of the component, and any changes to that variable will be read to a screen reader once (as well as any time the parent container becomes tab-focused).这可以通过将父级上的变量设置为组件的v-model来使用,并且对该变量的任何更改都将被读取到屏幕阅读器一次(以及在父容器成为以标签为焦点的任何时候)。

It can also be triggered by this.$refs.component.say(textToSay);也可以通过this.$refs.component.say(textToSay);触发-- note this will also be triggered again if the parent container becomes tab-focused. -- 请注意,如果父容器以选项卡为中心,这也将再次触发。 This behavior can be avoided by putting the element within a container that will not receive focus.通过将元素放在不会获得焦点的容器中,可以避免这种行为。

It also includes a slot so text can be added like this: <assistive-text>Text to speak</assistive-text> however, that should not be a dynamic/mustache variable or you will encounter the problem in the original question when the text changes.它还包括一个插槽,因此可以像这样添加文本: <assistive-text>Text to speak</assistive-text>但是,这不应是动态/胡子变量,否则您将在原始问题中遇到问题,当文字变化。

I've also updated the sandbox posted in the question with a working example of this component.我还使用此组件的工作示例更新了问题中发布的沙箱

Thank you for sharing the component. 感谢您分享组件。 My solution is far less robust, but the simplicity serves my needs. 我的解决方案不够健壮,但是简单性可以满足我的需求。

This is a custom directive, v-aria-live , which is added among aria attributes on a live region. 这是一个自定义指令v-aria-live ,已将其添加到实时区域的aria属性中。 It does nothing but momentarily emptying out the content area before updating it. 它什么也没做,只是在更新之前暂时清空内容区域。 That's all. 就这样。

It operates on the suggested practice of emptying out the aria-live region before updating , to better ensure that screen readers detect the change, especially if the container is updated with the same contents and should be announced again. 它按照建议的做法进行操作,即在更新之前清空aria活动区域 ,以更好地确保屏幕阅读器检测到更改,尤其是如果容器使用相同的内容进行更新并且应再次宣布。

Here is an example of how it might be used as a local directive: 这是一个如何将其用作本地指令的示例:

directives: {
    ariaLive: {
        componentUpdated(el) {
            var html = el.innerHTML;
            el.innerHTML = '';
            setTimeout(() => {
                el.innerHTML = html;
            }, 0);
        }
    }
},

And a fiddle of a simplified Vue app demonstrating how it works: 一个简化的Vue应用程序的小提琴,演示了它的工作原理:

https://jsfiddle.net/gyx6vzb7/ https://jsfiddle.net/gyx6vzb7/

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM