直接给容器加 aria-live 就够了:选 polite 或 assertive 取决于干扰程度,禁用 innerHTML 改用 textContent,避免高频更新与语义嵌套,确保跨 AT 兼容。

直接给容器加 aria-live 就够了,别套 role="region"、别设空 aria-label、别用 innerHTML 更新——这是实时区域无障碍播报能正常工作的最低可行配置。
aria-live 属性值选 polite 还是 assertive?
选哪个取决于信息对用户操作的干扰程度:
-
aria-live="polite":适合表单校验结果、加载完成提示、“保存成功”这类非中断性反馈;屏幕阅读器会等用户暂停当前朗读后再播报 -
aria-live="assertive":仅用于必须立即打断当前操作的信息,比如“网络已断开”“支付失败,请重试”;但滥用会导致频繁打断,体验反而更差 - 不要用
aria-live="off"作为默认值再动态切换——它不触发播报,只是关闭监听;真要手动控制,改完内容后立刻设回polite或assertive
为什么不能用 innerHTML 更新 live region?
因为屏幕阅读器只关心文本流变化,不是 HTML 渲染结果:
将 Markdown 文章转换为微信公众号兼容的纯 HTML 格式。当用户要求"转成微信格式"、"生成公众号文章 HTML"、"排版到微信草稿箱"、"微信粘贴格式"时触发。输出带内联样式的纯 HTML,微信编辑器可直接渲染,无需额外适配。支持标题、段落、列表、粗体、行内代码、表格、引用块、代码块、配图的完整转换。
-
el.innerHTML = "<strong>失败</strong>"可能被跳过、截断,甚至完全静音——不同 AT(如 NVDA/JAWS/VoiceOver)对标签的解析策略差异很大 -
el.textContent = "失败"才是安全写法,确保纯文本变更被稳定捕获 - 如需视觉强调(比如标红“失败”),用 CSS 控制
color或background-color,而不是插入<span>或<strong> - 换行需求可用
white-space: pre-line+\n,但得实测 VoiceOver 是否正确分句朗读
高频更新导致播报失效或重复怎么办?
live region 不是事件总线,它是为“状态变更”设计的,不是为“每帧刷新”准备的:
立即学习“前端免费学习笔记(深入)”;
- 倒计时、进度条等高频场景,应降频到“值真正变化时才更新”,比如从 10% → 15% 才赋值,而不是每 100ms 都写一次
textContent - ChromeVox 对微小变更(如多一个空格)极其敏感,可能反复重读;VoiceOver 则倾向合并 100ms 内多次赋值,只读最后一次——这不是 bug,是策略差异
- 测试必须覆盖至少三种组合:
Chrome + ChromeVox、Safari + VoiceOver、Edge + NVDA;只测一种 AT 容易漏掉兼容问题 - 临时隐藏 live region 时,禁用
display: none或visibility: hidden,改用aria-hidden="true"并同步控制视觉可见性
最常被忽略的点是:live region 的容器本身不能被 CSS 隐藏,也不能嵌套在 role="region" 里——语义叠加反而会让 AT 误判结构。它就该是一个干净的、带 aria-live 的普通元素,别的都多余。


















