J5.09.4virtual buffer refresh设计研究

动态更新内容后缓冲区需要重建,否则播报的是过时内容

别名: 缓冲区重建 · 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。

延伸

  • 同组J5.09.1 阅读器维护一份独立于视觉渲染的虚拟内容缓冲区 · J5.09.2 浏览模式与焦点模式的切换决定按键的行为方式 · J5.09.3 缓冲区的内容顺序基于文档结构而非样式表定义的视觉位置
  • 相邻J5.12 动态内容的播报 · J5.07 兼容性测试
  • 站内检索virtual buffer refresh · off-screen model · stale buffer

同组卡片

快捷操作

分享

分享当前页面

ios_share

https://hci.top/zh/handbook/J5.09.4