必须手动加tabindex="0"并监听Enter/Space键事件,否则仅设role="button"无法聚焦和触发;按钮无文字时,有可见稳定文本用aria-labelledby,否则用aria-label。

button 元素本身对屏幕阅读器是友好的,但实际项目中大量出现“伪按钮”(div、span、a)或语义正确但交互缺失的 button,这才是问题根源。
为什么用 div 绑 click 就算加了 role="button" 也不够
加 role="button" 只是告诉屏幕阅读器“这是个按钮”,但不等于它能被键盘操作。用户按 Tab 进不去,按 Enter 或 Space 不触发,等于功能残缺。
- 必须手动加
tabindex="0",否则无法获得焦点 - 必须监听
keydown事件,显式处理Enter和Space键(注意:Space要preventDefault(),否则会触发页面滚动) - 若按钮有状态(如加载中),还得同步更新
aria-disabled和aria-busy,否则读屏用户不知道操作是否生效 - 更稳妥的做法:直接用
<button type="button">,浏览器自动处理焦点、键盘响应、禁用逻辑和语义角色
button 没文字时,aria-label 和 aria-labelledby 怎么选
核心判断依据是:按钮旁有没有**可见且稳定**的文本。有,就用 aria-labelledby;没有,才用 aria-label。
-
aria-labelledby="icon-desc":适合图标按钮旁有隐藏但可访问的说明文字,比如<span id="icon-desc" class="sr-only">删除这条评论</span>。屏幕阅读器会读出该元素的全部文本,且支持多元素组合引用(如aria-labelledby="icon-title icon-desc") -
aria-label="删除":适合纯符号按钮(如 ×、⋯)、工具栏图标等无上下文场景。注意它会完全覆盖按钮内部文本——写成<button aria-label="关闭">×没问题,但<button aria-label="关闭">关闭</button>会导致“关闭”二字被静音 - 避免用
title属性替代:它只在悬停时显示,对键盘/触摸/读屏用户无效
常见错误:按钮在表单里却没 type 属性
没写 type 的 button 在表单内默认是 submit,这常导致意外提交;而屏幕阅读器通常不会播报这个隐式行为,用户按了才知道“怎么页面跳走了”。
立即学习“前端免费学习笔记(深入)”;
- 明确声明
type="button"(普通操作)、type="submit"(提交表单)、type="reset"(重置) - 如果是
type="submit",确保父级form存在且结构完整,否则读屏可能无法关联到对应表单域 - 禁用状态统一用
disabled布尔属性,而不是仅靠aria-disabled="true":前者会禁用焦点、键盘响应和视觉反馈,后者只是“说它不可用”,实际仍可聚焦并触发事件
lang 和 aria-live 对按钮体验的影响常被忽略
按钮文案含外文术语(如“Save as PDF”、“API 密钥”)时,根 lang="zh-CN" 会让屏幕阅读器强行用中文发音规则读英文,造成理解障碍。
- 在按钮内包裹外文词时,用
<span lang="en">PDF</span>显式标注语言,触发读屏切换语音引擎 - 按钮点击后弹出成功提示(如 Toast),必须用
aria-live="polite"包裹,否则读屏用户根本不知道操作结果;不要依赖 CSS 动画或定时器自动消失——它们对辅助技术不可见 - 动态生成的按钮(如 React 中
map渲染)需确保每次渲染都带一致的aria-label或aria-labelledby,避免因 key 变化或条件渲染导致描述丢失



















