aria-valuenow必须配合role="progressbar"且同时设置aria-valuemin和aria-valuemax才能被屏幕阅读器正确播报;单独使用或缺失范围属性将导致静默或“进度未知”。

aria-valuenow 本身不会自动触发朗读,必须配合 role="progressbar"
单纯给任意元素加 aria-valuenow="65",屏幕阅读器根本不会理它——这个属性只是“数据”,不是“语义”。只有当元素被明确标记为进度条(role="progressbar"),且满足可访问性上下文时,屏幕阅读器才会主动读出当前值。常见错误是把它加在 <div> 或 <code><span></span> 上却没设 role,结果数值完全静默。
实操要点:
-
role="progressbar"是硬性前提,缺一不可 - 必须同时提供
aria-valuemin和aria-valuemax,否则部分读屏(如 NVDA)会跳过aria-valuenow - 建议加上
aria-label或aria-labelledby明确说明用途,比如aria-label="文件上传进度" - 不要用
title替代aria-label,很多读屏默认忽略title
动态更新 aria-valuenow 后,读屏不一定立刻朗读
JavaScript 修改 aria-valuenow 的值(比如从 30 改成 45),读屏不会自动播报——它只在焦点进入、或用户手动触发(如按 Tab 进入该元素)时读一次当前状态。想实现“边更新边播报”,得靠 aria-live="polite" 配合辅助文本容器。
推荐做法:
- 在进度条旁放一个隐藏的实时提示区:
<div aria-live="polite" aria-atomic="true" class="sr-only">已上传 45%</div>
- 每次更新
aria-valuenow时,同步更新这个区域的文本内容 -
class="sr-only"用 CSS 隐藏但保留在读屏流中(position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;) - 避免用
aria-live="assertive",它会中断当前朗读,体验生硬
ChromeVox / VoiceOver / NVDA 行为差异明显
同一段代码,在不同读屏+浏览器组合下表现不一。比如:
- VoiceOver + Safari:通常在获得焦点时读“文件上传进度,65%,进度条”,不读 min/max
- NVDA + Firefox:若缺
aria-valuemin,可能直接跳过整个元素;若用了aria-labelledby指向一个 visible label,会连 label 一起读 - ChromeVox:对
aria-live区域响应较快,但对纯role="progressbar"的静默更新几乎无反应 - 所有读屏都要求元素可聚焦(
tabindex="0"或原生可焦元素如<input type="range">),否则无法通过键盘访问
更简单可靠的替代方案:用
原生 <progress></progress> 标签自带完整可访问性支持,浏览器自动映射到 role="progressbar" 并处理 aria-valuenow 等逻辑,无需手动补全。
直接写:
<progress value="65" max="100">65%</progress>
注意:
- 内部文本(如“65%”)是 fallback,仅在不支持
<progress></progress>的老浏览器中显示,不影响读屏行为 - 仍建议加
aria-label(如<progress aria-label="备份完成进度"></progress>),因为默认朗读可能只说“65%”,缺乏上下文 - 不要用
<meter></meter>替代,它是用于标量测量(如磁盘使用率),语义和读屏行为都不同
aria-valuenow 的“可读性”从来不是单点设置问题,而是语义角色、边界属性、动态反馈机制和浏览器/读屏兼容性四者咬合的结果。最容易被忽略的是:没有 aria-valuemin/aria-valuemax 时,很多读屏直接放弃解析 aria-valuenow——这点在测试阶段常被漏掉。











