使用 aria-live 在 vue.js 中向 reader 屏幕发布信息
Announcing information to a screen reader in vue.js using aria-live
我正在尝试让 vue 组件在我的网站上发生不同事件时动态地向屏幕发布信息reader。
我让它工作到单击按钮将填充 aria-live="assertive"
和 role="alert"
范围内的文本。这第一次工作得很好,但是,单击具有类似行为的其他按钮会导致 NVDA 在阅读新文本之前阅读以前的文本两次。这似乎发生在 vue 中,但使用 jquery 的类似设置却没有发生,所以我猜这与 vue 呈现给 DOM.
的方式有关
我希望有某种方法可以解决此问题,或者可能有更好的方法将文本读给不会有此问题的用户。非常感谢任何帮助。
Here is a simple component 我在工作代码沙箱中设置以显示我遇到的问题(导航至 components/HelloWorld.vue 获取代码)-- 注意:此沙箱已根据以下答案更改。该组件的完整代码如下:
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>
好的,所以我发现更一致的工作方式不是用新文本替换元素中的文本,而是将新元素添加到包含要读取的新文本的父容器。我没有将文本存储为单个字符串,而是将其存储在一个字符串数组中,这些字符串将 v-for
放到 aria-live
容器内的页面上。
我已经构建了一个完整的组件,它将以各种方式执行此操作,作为任何想要执行相同操作的人的示例:
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>
这可以通过将父级上的变量设置为组件的 v-model
来使用,对该变量的任何更改都将被读取到屏幕 reader 一次(以及任何父容器变为以制表符为中心的时间)。
它也可以由 this.$refs.component.say(textToSay);
触发 -- 请注意,如果父容器以选项卡为中心,这也会再次触发。可以通过将元素放在不会接收焦点的容器中来避免此行为。
它还包含一个插槽,因此可以像这样添加文本:<assistive-text>Text to speak</assistive-text>
但是,这不应该是一个 dynamic/mustache 变量,否则当文本更改时,您将遇到原始问题中的问题.
我还使用此组件的工作示例更新了 the sandbox posted in the question。
我正在尝试让 vue 组件在我的网站上发生不同事件时动态地向屏幕发布信息reader。
我让它工作到单击按钮将填充 aria-live="assertive"
和 role="alert"
范围内的文本。这第一次工作得很好,但是,单击具有类似行为的其他按钮会导致 NVDA 在阅读新文本之前阅读以前的文本两次。这似乎发生在 vue 中,但使用 jquery 的类似设置却没有发生,所以我猜这与 vue 呈现给 DOM.
我希望有某种方法可以解决此问题,或者可能有更好的方法将文本读给不会有此问题的用户。非常感谢任何帮助。
Here is a simple component 我在工作代码沙箱中设置以显示我遇到的问题(导航至 components/HelloWorld.vue 获取代码)-- 注意:此沙箱已根据以下答案更改。该组件的完整代码如下:
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>
好的,所以我发现更一致的工作方式不是用新文本替换元素中的文本,而是将新元素添加到包含要读取的新文本的父容器。我没有将文本存储为单个字符串,而是将其存储在一个字符串数组中,这些字符串将 v-for
放到 aria-live
容器内的页面上。
我已经构建了一个完整的组件,它将以各种方式执行此操作,作为任何想要执行相同操作的人的示例:
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>
这可以通过将父级上的变量设置为组件的 v-model
来使用,对该变量的任何更改都将被读取到屏幕 reader 一次(以及任何父容器变为以制表符为中心的时间)。
它也可以由 this.$refs.component.say(textToSay);
触发 -- 请注意,如果父容器以选项卡为中心,这也会再次触发。可以通过将元素放在不会接收焦点的容器中来避免此行为。
它还包含一个插槽,因此可以像这样添加文本:<assistive-text>Text to speak</assistive-text>
但是,这不应该是一个 dynamic/mustache 变量,否则当文本更改时,您将遇到原始问题中的问题.
我还使用此组件的工作示例更新了 the sandbox posted in the question。