大多数浏览器和
Developer App 均支持流媒体播放。
-
网页辅助功能的新增内容
探索相关技巧,充分利用自定义控制、SSML 和对话框元素来构建内容丰富而且易于访问的网页版 App。我们将讨论不同的辅助技术,并帮助您了解如何在测试网页版 App 的辅助功能时使用它们。
资源
相关视频
WWDC21
-
下载
Tyler: 大家好 我是 Tyler 是来自 WebKit accessibility 团队的工程师 在今天的讲座中 我将带大家游览 现代网络的辅助功能 第一站是辅助技术的概览 如屏幕阅读器
然后 我们会介绍您如何 在 Web Speech API 和 dialog 元素中 使用如自定义控件 语音合成标记语言 或简称 SSML 等工具 构建丰富 可访问的 网页 App 的方法
我们先来看下辅助技术 全球大约 1/7 的人有某种残疾 影响他们与世界 设备 和网络之间的互动 不同年纪的人会经历 不同时间长度 不同严重程度的残疾 Apple 构建了多种工具 帮助用户以最佳的方式 来与设备互动 这些工具包括 VoiceOver 开关控制 语音控制 全键盘访问 这些都为用户提供了 不同的设备使用方式 您可以查看去年的 “Support Full Keyboard Access in your iOS app”讲座 以了解更多关于这些工具 及其它方面的内容
要了解这在网页上的 实际使用感受 我们用 VoiceOver 来处理 一个样本测试评估网站 在我的 iPad 上 通过连按三次 顶部按钮来激活 VoiceOver Siri: VoiceOver 开启 Safari 浏览器显示侧边栏 按钮
Tyler: 现在 激活 VoiceOver 后 我可以轻触页面顶部来查看 Siri: Pop Quiz 一级标题 Tyler: 向右滑动以在页面的 不同元素间跳转 Siri: 第 1 题 / 共 6 题 1/4 片披萨中 有多少片是总共有 8 片的 2 片 单选按钮 未勾选 第 1 个选项 共 4 个选项 3 片 4 片 6 片 下一题 按钮 Tyler: 作为一个网页开发者 有许多可供选择的工具 让您的页面可被用户访问 如 VoiceOver 例如 Safari 浏览器对语义 HTML 元素 如按钮 标题 h1 到 h6 表格元素 列表元素等 都有内置的易访问性支持 使用这些语义 HTML 元素 应作为您的默认设置 因为它能确保您的用户 在所有浏览器上 都有一致的 可访问的体验 然而 有时语义 HTML 并不一定 能完全满足您的需求 需要用 JavaScript 来 创建自定义组件 如果是这种情况 您可能还需要 使用 ARIA 属性 来补充您的组件 这样它们的语义才能恰当 传达到辅助技术中 这就引出了我们今天的 第二个话题 自定义控件 假如我们想要让这个披萨测试题 更引人入胜 其中一个方法是用自定义控件 来替换单选按钮 从而让用户通过轻触 来添加和移动披萨切片 这种自定义控件的标记符号 以 div 和 ID 开始
为了让用户通过轻触或点击组件 就能进行交互 我们需要添加一个 点击事件监听器 我们用构造器来创建一个 新的 PizzaControl 类 可以接受元素的 ID
我们通过 ID 获取该元素 然后为其添加点击事件监听器
该监听器根据轻触的位置 计算新的切片数量 然后将数量传递给调用更新的函数 从而重新渲染控件 这对一些用户来说很有用 但并不是全部
比如 对于有视力障碍的用户来说 他们不知道要点击或轻触哪里 那怎么办呢 构建自定义组件时 我们一定要 考虑到所有类型的 辅助技术用户 都会与我们的组件互动 从这一点出发 创建可访问组件的 第一步就是 为其提供一个角色属性 值为“slider” 我们的控件能完美地 映射到 slider 模型 有一个最小值 0 片 和最大值 8 片 当前值是 4 片 我们还需要添加 tabindex 为 0 确保使用键盘 和其它非触屏界面的用户 也能在组件上设定焦点
我们也需要添加几个 ARIA 属性
Aria-valuemin 和 aria-valuemax 可提示辅助技术 该 slider 的最小值 和最大值
这些属性与您在 本机范围类型输入中使用的 最小属性和最大属性类似
接下来 我们添加 aria-valuenow 以传递控件的当前值
我们还要使用 aria-valuetext 提供对当前值 更有用的描述 即 4 片
现在我们已经将控件设定为 可定焦 slider 我们需要在辅助技术中 处理控件值的更新 在 iOS 中 VoiceOver 通过 单指上滑来增加 slider 单指下滑减少 slider 从而帮助调整 sliders Safari 浏览器提供了 处理这些手势的简易方法 当 VoiceOver 用户上滑 定焦一个 slider 时 Safari 浏览器自动模拟 右箭头键事件 同样的 如果 VoiceOver 用户下滑 定焦一个 slider 时 则模拟左箭头键事件 这些模拟事件的行为 与真实键盘按键一样 意味着它们可以通过 键盘事件监听器来处理 有了工具带的这一新知识 我们可以为披萨控件 添加一个键盘按下监听器 如果激活的按键是右箭头 或上箭头 我们将会更新控件为 当前切片数量加 1 同样的 如果激活按键是 左箭头或下箭头 我们将会更新控件为 当前切片数量减 1 增加这个关键事件监听器 不仅对 VoiceOver 用户有利 也对全键盘访问用户有利 他们可能会严重或完全依赖于 您的网页 App 变得键盘可访问 建立了两种事件监听器后 我们现在也要定义更新的函数了 首先 我们要限定所给的数值 使其保持在 0 至 8 的限值内 将我们存储的切片数量状态 更新为该值 接下来 我们要确保更新了 控件的视觉和 ARIA 呈现 当构建自定义组件时 一条黄金规则是 如果您更新 组件的视觉呈现 也要考虑如何更新 ARIA 呈现
在这种情况下 我们需要 同时更新 aria-valuenow 和 aria-valuetext 属性 从而告知辅助技术用户 新的控件状态
首先要设置 aria-valuenow 为当前切片数量
接下来 设置 set aria-valuetext 为更用户友好的 切片数量描述方式 加上“slice”或“slices” 好了 现在一切准备就绪 我们回到测试评估网页 App 来看下 VoiceOver 下的 用户体验 我首先轻触披萨控件以定焦 Siri: 4 片 可调整 单指上滑或下滑以调整数值 Tyler: 我们听到了 VoiceOver 说出了 slider 的初始值 4 片 且这是可调整的 跟着 VoiceOver 的提示 我们可以上滑来增加 选定切片的数量 Siri: 5 片 6 片 Tyler: 下滑来减少选定切片的数量 Siri: 5 片 4 片 Tyler: 这些变化就绪后 我们的自定义 slider 组件 辅助功能提升了 现在 我们来看下您可以如何使用 Web Speech API 中的 SSML 来为您的所有用户 构建更丰富的体验 Web Speech API 是由 两个主要 JavaScript 界面组成的 音频输入的 SpeechRecognition 以及文本-音频输出的 SpeechSynthesis
Web Speech 让您的网页 App 可提供语音辅助 或纯语音交互界面
这对有运动残疾的用户来说 是很有利的 因为他们可能无法使用 其它方式的控件 如鼠标 键盘或触屏 Safari 浏览器中 SpeechSynthesis 的 新功能就是可使用 SSML 从而改变您文本的朗读方式 SSML 有众多功能 例如 您可以使用 break 元素 来在讲话中插入 您选择的暂停时长 您会让您的用户吸气 和呼气
使用 phoneme 元素 您可以控制 单词的发音 如“tomayto” 和“tomahto”之间的区别
prosody 元素让您可以 操控朗读文本的音高 语速和音量 这些只是 SSML 众多功能中的 沧海一粟 您可以查看 w3.org 中的 SSML 规范以了解更多信息 我们来实际应用新发现的 SSML 知识 测试的最后一道问题 我们让学生 通过单选按钮来选择 “the water” 西班牙语的正确翻译 我们也可以将这个问题调整得 更具吸引力 可以让用户 点选按钮 通过文本-语音的方式 来朗读和回答问题 通过 SSML 用西班牙地区特性的语音 来朗读西班牙词组 我们首先创建按钮 确保说话人的表情符号 限制在一定范围内 将 aria-hidden 设置为 true 因为表情符号的描述 在这里并不一定有用
接下来 我们创建一个 helper JavaScript 函数 称为 wrapWithSSML 它会选择一个词组 用具有地区特性的语音来朗读
我们先是用 break 元素 构建 SSML 字符串 从而在每个词组前插入 短暂的停顿以强调
通过 prosody 元素 我们可以指定词组 以默认语速 80% 的 速度来朗读
最后 我们使用 lang 元素 来选择 希望词组使用的 特定地区特性声音
现在我们为朗读问题按钮 添加单击事件监听器 以及构建内部 SSML 字符串 首先将整个字符串 都放置于 speak 元素内
Speak 很重要 因为它可以 提示合成处理器 在这个范围内的所有内容 都应通过 SSML 来处理
加下来 我们加入问题 “the water 西班牙语怎么说” 我们可以用 wrapWithSSML helper 函数 为需要翻译的词组添加强调 确保其用英语地区语音来朗读
四个可能的答案同样 使用了 wrapWithSSML 提供强调 并提出使用 西班牙地区语音来朗读的请求
最后 我们可以用 SSML 字符串 来创建新的 SpeechSynthesisUtterance 对象 并将其传送到 SpeechSynthesis 方法视窗 以朗读
这些都准备就绪后 我们看下在网页 App 中 是怎样的体验 在最后一道问题的页面中 我轻触“Read question”按钮 并听取语音 Siri: “the water”西班牙语怎么说 El agua La abuela La abeja El árbol Tyler: 多亏了 SSML 我们为学生创建了 更引人入胜的体验
网页另一个常用的设计模式 是 modal
您的网页 Apps 中可能会 将其用于注册或登陆表单 来确认 dialogs 等等
提供可访问 modal 体验的 一个方法 就是 aria-modal 属性 使用 aria-modal="true" Safari 浏览器会考虑 modal 以外 所有被忽略的可访问元素 最近 Safari 浏览器还新增了 支持 dialog 元素的功能 Dialog 通过标准定焦交互 对 modal 关闭手势的创新处理 如 iOS 上的 Escape 键 和擦除手势等 让提供可访问友好的 modal 体验更为简单 要看它们的实际运行情况 我们将测试评估网页 App 上的 “Show score”按钮改为 打开带有结果的对话
首先 我们需要创建 dialog 元素 标记符号可以是这样的 我们为 dialog 赋予一个 ID 之后可以被 show score 按钮引用 我们还将 dialog 的内容 置入有 method dialog 的表单中
这样操作后 任何提交类型的控件 如我们的按钮 都会导致对话关闭 要打开 modal 我们还需要一点 JavaScript 我们在 Show Score 按钮上 添加点击事件监听器 在我们的 dialog 元素上 调用 showModal()
现在我们已万事俱备 VoiceOver 激活后 我轻触 “Show score”按钮以定焦
Siri: Show score 按钮
Tyler: 然后 我在屏幕任意位置 单指双击以点选按钮 Siri: Show score 网页对话 Close 按钮 Tyler: 现在我们的 modal 就完成了 我可以左滑以在 modal 的内容上 移动 从而听取分数 Siri: 您答对了 6 个问题 太棒了 Tyler: 完成后 我可以通过右滑 回到 close 按钮 Siri: Close 按钮 Tyler: 双击以关闭 modal Siri: 未勾选 Tyler: 如我之前提到 dialog 元素可处理 iOS 擦除手势 从而立即关闭 modal 我给大家演示下 首先重新 双击打开 modal Siri: Show score 按钮 网页对话 Close 按钮 Tyler: 然后使用擦除手势 双指快速在屏幕上向右 向左 再向右移动
Siri: Show score 按钮 Tyler: 好了 这样我们就有了一个 功能 modal 但我们还能做到更好 您有没有注意到 当我们打开 modal 时 VoiceOver 只会朗读 “网页对话 close 按钮” 在这种情况下 使用 aria-label 或 aria-labelledby 属性 为辅助技术用户提供更多信息 将会更有意义 由于我们的 modal 内容很短 只要告知用户 他们答对了多少个问题即可 我们用标签来完成 首先 将 modal 内容 用 ID 将其限制在一个范围 然后 我们可以添加 aria-labelledby 属性 到 dialog pointing 到 modal 内容 ID
我们还要明确地把初始 modal 定焦元素 设置为带有自动定焦属性的 close 按钮
虽然这已经是这一简单 modal 的 默认行为 如果我们的 modal 有很多内容 或更复杂 有很多控件 那情况就不一样了
例如 在有很多内容的 modal 中 最好是在最上方的标题栏 加入自动定焦 作为 modal 作者 您最为清楚什么内容 会给您的用户带来完美体验
有了我们的新属性 我们再来看看 在 VoiceOver 中的体验如何 我首先单击 Show score 按钮以定焦 Siri: Show score 按钮
Tyler: 然后双击来点选 Siri: 您 6 个问题全部答对了 太棒了 网页对话 close 按钮 Tyler: 这个体验更完美 多亏了 aria-labelledby VoiceOver 用户马上就能听到他们的分数 且已经定焦至 close 按钮 他们可以直接双击 以离开 modal 现在我们来总结下今天的讲座 我希望大家都学到了如何构建 丰富 可访问的网页 App 技术 从而为您所有用户带来 完美的体验 请在最新的 Safari 浏览器中 尝试这些功能 如果有任何 bug 请登录 bugs.webkit.org 上传至 WebKit bug tracker 感谢大家参与今天这场 简要的现代网页辅助功能旅程 祝您的 WWDC 之旅一切顺利
-
-
3:06 - PizzaControl class with click event listener
class PizzaControl { constructor(id) { this.control = document.getElementById(id); this.sliceCount = 4; this.control.addEventListener("click", (event) => { const newSliceCount = this.computeSliceCount(event); this.update(newSliceCount); }); } }
-
4:23 - PizzaControl HTML markup
<div id="pizza-input" role="slider" tabindex="0" aria-valuemin="0" aria-valuemax="8" aria-valuenow="4" aria-valuetext="4 slices"> </div>
-
5:15 - PizzaControl class with keydown event listener
class PizzaControl { constructor(id) { this.control = document.getElementById(id); this.sliceCount = 4; // …click event listener… this.control.addEventListener("keydown", (event) => { const key = event.key; if (key === "ArrowRight" || key === "ArrowUp") this.update(this.sliceCount + 1); else if (key === "ArrowLeft" || key === "ArrowDown") this.update(this.sliceCount - 1); }); } }
-
5:41 - PizzaControl class update function
class PizzaControl { // …constructor… update(newSliceCount) { this.sliceCount = Math.max(0, Math.min(newSliceCount, 8)); // Visually re-render `this.sliceCount` slices // … // Update the ARIA representation of the control this.control.setAttribute("aria-valuenow", this.sliceCount); const sliceModifier = this.sliceCount === 1 ? "slice" : "slices"; this.control.setAttribute("aria-valuetext", `${this.sliceCount} ${sliceModifier}`); } }
-
7:52 - SSML examples
<speak> Breathe in <break time="3s"/> and breathe out. </speak> <speak> <phoneme alphabet="ipa" ph="təˈmeɪtoʊ">tomato</phoneme> <phoneme alphabet="ipa" ph="təˈmɑːtəʊ">tomato</phoneme> </speak> <speak> <prosody pitch="-2st" rate="slow" volume="loud"> Hello world! </prosody> </speak>
-
8:45 - "Read question" button HTML markup
<button id="read-question-btn"> Read question<span aria-hidden="true">🔊</span> </button>
-
8:57 - wrapWithSSML JavaScript function
function wrapWithSSML(phrase, locale) { return ` <break time=“100ms"/> <prosody rate=“80%"> <lang xml:lang="${locale}"> ${phrase} </lang> </prosody> `; }
-
9:24 - Read question button click event listener
const readQuestionButton = document.getElementById("read-question-btn"); readQuestionButton.addEventListener("click", () => { const ssml = ` <speak> How do you say ${wrapWithSSML("the water", "en-US")} in Spanish? ${wrapWithSSML("El agua", "es-MX")} ${wrapWithSSML("La abuela", "es-MX")} ${wrapWithSSML("La abeja", "es-MX")} ${wrapWithSSML("El árbol", "es-MX")} </speak> `; const utterance = new SpeechSynthesisUtterance(ssml); window.speechSynthesis.speak(utterance); });
-
11:33 - Show score dialog HTML markup
<dialog id="show-score-modal"> <form method="dialog"> You got all six questions correct. Great work! <button type="submit">Close</button> </form> </dialog>
-
11:51 - JavaScript to open show score dialog
const showScoreButton = document.getElementById("show-score-btn"); showScoreButton.addEventListener("click", () => { document .getElementById("show-score-modal") .showModal(); });
-
13:23 - Show score dialog with autofocus and aria-labelledby attribute
<dialog id="show-score-modal" aria-labelledby="modal-content"> <form method="dialog"> <span id="modal-content"> You got all six questions correct. Great work! </span> <button type="submit" autofocus>Close</button> </form> </dialog>
-
-
正在查找特定内容?在上方输入一个主题,就能直接跳转到相应的精彩内容。
提交你查询的内容时出现错误。请检查互联网连接,然后再试一次。