role="spinbutton"需配套aria-valuenow、aria-valuemin、aria-valuemax及tabindex="0",并手动监听键盘事件更新值;原生input[type="number"]须显式设role="spinbutton"才能被正确识别为数字微调器。

role="spinbutton" 的基本用法和必要配套属性
单纯写 role="spinbutton" 不会触发任何可交互行为,浏览器只把它当语义标记,不会自动绑定键盘操作或焦点管理。必须同时提供 aria-valuenow、aria-valuemin、aria-valuemax,且元素得是可聚焦的(比如加 tabindex="0" 或本身是 <input>)。否则屏幕阅读器读不出当前值,键盘 ↑/↓ 也无效。
-
aria-valuenow必须是数字字符串(如"42"),不能是空或"auto" - 如果用
<div> 实现,一定要加 <code>tabindex="0";用<input type="number">更省事,但需手动补role="spinbutton"(因为原生 role 是textbox) - 别漏掉
aria-labelledby或aria-label,否则辅助技术不知道这个控件叫什么 - 必须手动监听
keydown,识别ArrowUp/ArrowDown/PageUp/PageDown - 每次变更后,要同步更新
aria-valuenow和 DOM 文本内容(比如textContent或value属性) - 记得处理边界:到达
aria-valuemin或aria-valuemax时停止递增/递减 - 不要依赖
onchange——它只在失焦或回车时触发,无法响应实时键盘微调 - 原生 input 的
step属性不影响 ARIA 行为,aria-valuenow的变更仍需 JS 控制 - 某些浏览器(如旧版 Safari)对
role="spinbutton"支持弱,建议 fallback 到role="textbox"+ 明确提示“按 ↑↓ 键调整” - 移动端没有
↑/↓键,得靠点击微调按钮触发,所以按钮的aria-controls要指向目标元素 - 修改
aria-valuenow后,最好紧接着触发一次aria-live="polite"区域播报(比如用一个隐藏的<div aria-live="polite"></div>插入当前值) - 别用
innerHTML更新显示值——某些屏幕阅读器不监听该变化;优先用textContent或value属性 - 确保微调按钮有
aria-disabled="true"状态(比如到最大值时禁用上键按钮),否则用户会误触无效操作
键盘交互失效的常见原因
按 ↑ 键没反应?大概率是没监听 keydown 事件,或者没调用 event.preventDefault()。原生 <input type="number"> 虽有微调箭头,但键盘方向键默认不生效——这是有意设计,避免和表单内其他焦点移动冲突。
与原生 input[type="number"] 的关键差异
原生 <input type="number"> 自带微调按钮和基础键盘支持,但它的 role 默认是 textbox,不是 spinbutton。想让辅助技术正确识别为“数字微调器”,必须显式覆盖:role="spinbutton",并补全所有必要 ARIA 属性。
无障碍测试时容易忽略的细节
用 NVDA 或 VoiceOver 测试时,常发现值读不出来,或说“未定义”。问题往往不在 role 本身,而在动态更新时没触发可访问性树刷新。
真正麻烦的不是写对 role,而是让每一次数值变化都被辅助技术即时、准确地感知到。这需要属性更新、DOM 同步、事件响应三者严丝合缝,少一个环节,就等于对部分用户关上了门。











