aria-valuemin 和 aria-valuemax 必须成对出现且与 aria-valuenow 配合使用,三者缺一不可;aria-valuenow 须为实时更新的数字,代表原始任务量而非百分比,width 与 aria-valuenow 需同步更新,完成时应移除 role="progressbar" 并切换语义状态。

aria-valuemin 和 aria-valuemax 必须成对出现,且不能省略 aria-valuenow
只写 aria-valuemin="0" 或只写 aria-valuemax="100",屏幕阅读器(如 NVDA、JAWS、VoiceOver)会直接跳过该元素,或报“进度未知”。aria-valuenow 是唯一动态值,必须实时更新,且必须是数字类型(不是字符串 "75",更不能带单位如 "75%")。
常见错误现象:
<div role="progressbar" aria-valuenow="75"> —— 缺 <code>aria-valuemax,读屏器静默<div role="progressbar" aria-valuemax="100"> —— 缺 <code>aria-valuenow,读屏器默认读“0%”,但你根本没设初值数值范围不是百分比,而是原始任务量的标尺
aria-valuemin和aria-valuemax定义的是当前任务的**原始数值区间**,不是“0% 到 100%”。浏览器内部用aria-valuenow ÷ aria-valuemax算出播报百分比,但语义上它代表真实业务量。使用场景与参数差异:
- 文件上传:已传字节 / 总字节 → 设
aria-valuemin="0"、aria-valuemax="2097152"(2MB)、aria-valuenow="1258291"(1.2MB) - 多步表单:第 3 步 / 共 8 步 → 设
aria-valuemin="1"、aria-valuemax="8"、aria-valuenow="3"(显式设 min=1 更准确) - 耗时任务(如部署):已运行秒数 / 预估总秒数 → 可设
aria-valuemin="0"、aria-valuemax="120"、aria-valuenow="47"
别强行换算成 100:后端返回
{ loaded: 0.78, total: 1 },应先转整数:Math.round(loaded * 100)再赋值,避免aria-valuenow="0.78"在部分 Android TalkBack 中被截断为 0。width 和 aria-valuenow 必须手动同步更新
视觉进度靠 CSS 控制(如
style.width),无障碍状态靠 ARIA 属性控制,二者完全解耦。只改width,读屏器仍报旧值;只改aria-valuenow,用户看到的还是旧长度。正确做法(JS 示例):
const bar = document.querySelector('.progress-bar'); const val = 82; const max = 100; <p>bar.style.width = (val / max) * 100 + '%'; bar.setAttribute('aria-valuenow', val);</p>注意点:
- 别用
transform: scaleX()替代width—— 它不改变盒模型尺寸,自动化测试和读屏器无法感知真实进度 - 更新频率高时(如每 50ms),避免在长任务中连续赋值;可用
requestAnimationFrame或setTimeout(..., 0)分帧更新 - 服务端返回小数进度(如
0.632),统一乘 max 后取整:Math.round(0.632 * 100) === 63
完成时别卡在 100%,要明确宣告结束
当任务真正完成,
aria-valuenow应等于aria-valuemax,但更重要的是:是否继续保留role="progressbar"?多数情况下,完成即意味着状态终结。此时建议:
- 保持
aria-valuenow为最大值,但移除role="progressbar",改用role="status"或仅留文本(如“上传完成”) - 若需保留视觉样式,可加 class 如
is-complete并禁用交互 - 避免让屏幕阅读器持续播报“100% 进度条”——这暗示任务仍在进行中,语义矛盾
最易被忽略的细节:完成态的无障碍上下文需要重新建立。比如原用
aria-labelledby="upload-title"指向“正在上传”,完成时应同步更新关联的描述文字,或切换aria-describedby指向成功提示。- 文件上传:已传字节 / 总字节 → 设











