补充属性会覆盖原生语义,用在原生元素上可能产生冲突而非补充
别名: role 覆盖 · 原生语义冲突 · ARIA 覆盖
概念解释
role 写在原生元素上,不是往上叠一层说明,而是换掉宿主语言已经暴露的角色。<button role="heading"> 在无障碍树里变成标题,不再是按钮;<h2 role="none"> 标题语义被撤掉。补充属性的这个机制叫覆盖(ARIA role override)。作者以为在「补充」,树上发生的是替换。原生键盘行为、默认名称计算、阅读器的元素列表筛选,会按新角色走,或卡在「外观还是按钮、角色已经不是」的夹缝里。
这里要讲的是覆盖如何发生、冲突长什么样,不是「能用原生就用原生」那句选型口号。
机制
无障碍树映射先看显式 role,再看宿主默认。显式值非空,默认角色被丢掉,不会合并成「按钮兼标题」。role="presentation" / none 是故意的删除;其它角色是改派。冲突来自改派之后,宿主还留着自己的行为:<a role="button"> 仍被 Enter 激活,但空格不一定,阅读器却按按钮的合同去等空格;<button role="link"> 的反向也成立。名称计算也可能换槽:按钮吃子文本,某些角色改吃 aria-label 或不再把子文本当名字。
第二层是阅读器的分类索引。用户按 B 找按钮、按 H 找标题、按 F 找表单。覆盖之后,元素从原来的名单消失、出现在另一张名单,或两张都不进。视觉上还是那个控件,浏览模式的快捷键已经对不上。
怎么研究
做最小对照:原生 button;同一 button 加 role="link";h2 加 role="button";button 加 role="presentation"。在检查器读角色,在 NVDA 用元素快捷键(B / H / K)看它出现在哪张列表,再试 Enter 与空格。VoiceOver 转子分类是另一套索引,一并记。
自变量:宿主元素、显式 role、是否 presentation。
因变量:树上的角色、快捷键列表归属、Enter/空格是否仍触发、计算名是否换源。
边界
少数补丁是故意覆盖:把无语义的容器改成 region,或用 role="text" 修破碎的字。表格上的 role="presentation" 用来拆掉布局表,这是删除不是改派,后果是子孙的单元格语义一并消失,波及范围比改一个按钮大。ARIA 1.2 之后部分 role 在某些元素上不合法,引擎可能忽略而不是覆盖,表现为「写了等于没写」。SVG、自定义元素的默认角色本来就弱,覆盖的相对伤害小,但不能反过来说「所以可以随便写」。
怎么落地
- 不要在已经有正确宿主角色的元素上再写一个不同的
role去「增强」。 - 必须改派时,按新角色补齐它的合同(名称、状态、键盘),并接受它会从旧的快捷键名单里消失。
- 用
presentation/none拆布局表或装饰列表时,检查子孙是否还需要单元格、列表项语义;需要就不要拆到那一层。 - 验证:检查器里读角色是否还是宿主默认。再用 NVDA 的 B 和 H 列表找这个控件——出现在错的名单、或两张都找不到,就是覆盖已经改了索引。最后按 Enter 和空格,行为与新角色合同不一致就是冲突而不是补充。