动态更新内容后缓冲区需要重建,否则播报的是过时内容
别名: 缓冲区重建 · off-screen model refresh · 过时播报
概念解释
虚拟缓冲区是一份拷贝。页面在没有整页刷新的情况下换掉主区域、改掉列表、用脚本写入新节点,拷贝不会自动变成新文档——阅读器要收到无障碍突变并重建缓冲区(virtual buffer refresh),虚拟光标再走才是现在的内容。重建没发生,用户按方向键听见的是上一版:已经拆掉的段落、已经不存在的按钮、已经换掉的标题。
这和「更新要主动喊一声」不是同一件事。不喊,用户可能不知道发生了变化;不重建,用户顺着旧地图走,会撞上幽灵节点。两条失败可以同时出现,机制不同。
机制
阅读器监听无障碍事件和 DOM 突变,再决定是局部补丁还是整页重拷。全量重建贵,所以会合并、延迟、有时干脆错过。框架如果用画布重绘、替换整个 iframe、在阅读器没订阅的层上改像素,事件管道是空的,缓冲区保持旧快照。JAWS 对网页缓冲更「黏」,NVDA 更跟树走,两家都会在单页应用切路由之后短暂念出上一页的骨架。
第二层是光标位置。重建不只换文本,还要决定虚拟光标落在哪。若焦点没被移到新内容,光标可能钉在已经消失的节点上,表现为沉默、跳到文档顶,或重复念一段不再存在的话。用户按阅读器的刷新命令(NVDA / JAWS 的 Insert+Esc 一类)是在人肉补上这次重建。
怎么研究
做一次「不整页刷新」的替换:点筛选、切路由、提交后就地换成结果列表。不要碰阅读器的刷新键,立刻用方向键从原位置往下走,对照无障碍检查器里此刻的树。再跑一档:替换后把焦点移到新标题。Windows 用 NVDA+Firefox 与 JAWS+Chrome;同一套单页应用两边的过时窗口往往长度不同。
自变量:更新方式(innerHTML 整块替换 / 逐节点补丁 / 画布重绘)、是否移动焦点、阅读器。 因变量:语音是否仍含旧节点、光标落点、是否必须手动刷新才能与检查器对齐。
边界
移动端阅读器对无障碍 API 的绑定更「活」,过时缓冲不如 Windows 网页那么典型,但不能推论「移动端从不念旧内容」——路由动画期间仍会夹一句上一屏。实时游戏、行情 tick 如果每帧都触发重建,阅读器会选择节流,这时过时是性能策略,不是 bug。跨源 iframe、影子根、扩展注入的节点,突变订阅经常断掉。用户若正停在输入模式里打字,阅读器可能推迟重建,以免把插入符打乱。
怎么落地
- 主内容被替换后,把焦点移到新区域的标题或容器,迫使树更新被阅读器看见,并给光标一个落点。
- 不要用整块画布或纯视觉过渡冒充内容切换;新内容必须作为无障碍节点出现,而不是只画出来。
- 长时间停留的单页应用,在关键路由切换后检查是否还在念旧地标。
- 验证:切一次客户端路由,立刻遮屏用方向键走,禁止按刷新。若仍听到上一页的标题或按钮,就是缓冲区没跟上。再在 JAWS 与 NVDA 各做一次,记下哪一家必须手动 Insert+Esc。