role属性唯一作用是告知屏幕阅读器元素的语义角色,仅在原生html无法准确表达时才需使用;滥用、冗余或错误设置会破坏无障碍体验。

role 属性不是用来“增强样式”或“让 JS 更好找元素”的,它的唯一作用是告诉屏幕阅读器:“这个元素在语义上扮演什么角色”。用错、滥用或覆盖原生语义,反而会破坏无障碍体验。
什么时候必须用 role 属性
只有当 HTML 原生元素无法表达你组件的真实语义时,才需要加 role。比如:
- 用
<div> 实现一个下拉菜单容器 → 加 <code>role="menu",否则屏幕阅读器只当它是普通区块 - 用
<span></span>模拟开关按钮 → 加role="switch"并配合aria-checked,否则用户无法感知状态 - 自定义弹窗遮罩层 → 加
role="dialog",并确保焦点捕获和aria-modal="true" - 页面顶部导航区域用
<div class="nav"> 包裹 → 改成 <code><nav></nav>更好;若受限于旧框架无法改标签,才用role="navigation"哪些 role 值容易误用
常见错误不是写错值,而是“多此一举”或“语义冲突”:
-
role="button"加在<button></button>上 → 原生<button></button>已自带完整语义和键盘行为,加了反而可能干扰辅助技术对默认行为的识别 -
role="link"加在<a href="#"></a>上 → 同样冗余,且若href为空或#,还会误导用户以为能跳转 -
role="article"加在<article></article>上 → 不仅多余,还可能覆盖原生<article></article>的隐含属性(如可被document.querySelector('article')正确匹配) -
role="none"或role="presentation"用于隐藏结构但保留子内容 → 必须确认子元素本身有足够语义,否则整块内容可能被完全跳过
role 和 aria-label 不是配套开关
role定义“它是什么”,aria-label定义“它叫什么”。两者可以共存,但不能互相替代:- 一个图标按钮
<button><svg>...</svg></button>没有可见文字 → 需要aria-label="删除项目",但不需要role="button" - 一个带标题的卡片区域:
<div role="region" aria-labelledby="card-title"> <h3 id="card-title">设置</h3>...</div>→ 这里role="region"是合理的(因无更贴切原生标签),而aria-labelledby提供名称,不是靠aria-label - 写了
role="alert"却没动态更新aria-live="polite"→ 屏幕阅读器可能完全不播报变化,role单独不触发实时通知
role 值的兼容性与运行时限制
不是所有
role值在所有浏览器+读屏组合中都表现一致,尤其涉及交互状态时:-
role="combobox"要求同时管理aria-expanded、aria-controls、aria-activedescendant等多个属性,漏掉任一关键状态,读屏就会卡住或报错“未知控件” -
role="tree"和role="grid"对键盘导航逻辑(方向键、Home/End、Ctrl+Shift+Enter)有严格要求,仅加role不实现对应 JS 行为,等于挂羊头卖狗肉 - 某些旧版 NVDA + IE11 组合不识别
role="feed"或role="doc-introduction",这类文档角色优先考虑降级为role="region"+aria-label - 所有
role值必须小写、拼写准确,role="Button"或role="menubar"(少个 u)会被直接忽略
最常被忽略的一点:role 属性一旦设置,就锁定了该元素的语义身份。后续 JS 动态切换
role值(比如从"tablist"改成"list")几乎不会被读屏重新解析——与其折腾 runtime 切换,不如用不同 DOM 结构或 CSS 控制显隐。 -











