J5.11.1ARIA relationship IDREF设计研究

用于建立元素间关系的属性(标签指向、描述指向)需要引用真实存在的元素

别名: aria-labelledby · aria-describedby · IDREF · 关系属性

概念解释

aria-labelledbyaria-describedbyaria-controlsaria-ownsaria-activedescendant 这类属性的值是 ID 列表,不是一段给人看的句子。它们要在同一棵文档里指到真实存在、计算时仍在树上的节点(ARIA relationship IDREF)。指向空、指向已卸载的节点、或指到另一棵影子树里读不到的 ID,关系在计算时被丢掉。失败是静默的:名称计算往下落,描述不出现,控件声称「控制着」一个不存在的区域。

机制

IDREF 在无障碍计算的那一拍被解析,不是在作者写属性时。单页应用把错误说明从 DOM 里卸掉、把 id 写在条件渲染的另一分支、把标签放进关闭的模板,属性字符串还在,解析结果是空。labelledby 空了,名称计算会落到下一项,看起来像「还行」,其实描述或组合名已经没了。controls 空了,阅读器无法提供「跳到被控区域」;activedescendant 空了,焦点仍在组合框外壳上,活动选项对阅读器是未知。

第二层是文档边界。ID 的作用域是树,不是屏幕。iframe、影子 DOM、跨层传送(portal)里的节点,外面的属性用同样的字符串对不上。复制组件时把 id="hint" 贴了三份,指向变成「哪一个都不保证」。关系属性没有「模糊匹配可见文字」的后备,写错就是断。

怎么研究

在检查器里看关系是否解析成节点,而不是看属性字符串在不在。构造四档:ID 存在且可见;ID 存在但 display:none;ID 在渲染后被删除;ID 在影子根内。对 labelledby / describedby 记录计算名与描述;对 controls 看阅读器能否跳转。框架路由切换后再查一次,专门抓卸载后的悬挂引用。

自变量:目标节点是否在树、隐藏手法、是否跨影子边界、ID 是否重复。 因变量:计算名/描述是否包含目标文本、关系在检查器中是否显示为断裂、阅读器跳转是否失败。

边界

指向视觉隐藏但未从树移除的节点是合法手法,用来提供计算名;这与指向已删除节点不同。aria-labelledby 允许指向多个 ID,其中一个断了时,有的引擎丢掉整串,有的跳过坏的继续拼,结果因引擎而异。SVG 内部的 id 与 HTML 文档的 id 是否互通,取决于嵌入方式。原生 <label for> 走宿主绑定,坏掉时浏览器常有可见的点击区域失败,比 ARIA 的静默更容易被看见——这不是「不要用补充属性」,是关系断裂在两条管道里的可见性不同。

怎么落地

  • 关系属性只填当前文档里确定会存在的 id;条件渲染的提示、错误、对话框要在挂上引用之后才写入属性,卸载时先清引用。
  • 组件生成 id 必须带实例前缀,禁止全局 hinterror 这类会碰撞的名字。
  • 跨影子根的关系用原生插槽或把目标放到同一棵可算的树上,不要假设字符串相同就能指过去。
  • 验证:在无障碍面板打开每个带 labelledby / describedby / controls 的节点,看引用是否解析成元素。再删掉目标节点或切走路由,听名称和描述有没有变空。字符串还在、面板显示断裂,就是在指幽灵。

延伸

  • 同组J5.11.2 实时区域属性的滥用会导致次要信息频繁打断当前播报 · J5.11.3 补充属性会覆盖原生语义,用在原生元素上可能产生冲突而非补充 · J5.11.4 补充属性无法给元素添加它本身不具备的键盘交互行为
  • 相邻J5.03 语义补充属性 · J5.10 名称、角色与状态
  • 站内检索ARIA relationship IDREF · aria-labelledby · aria-describedby

同组卡片

快捷操作

分享

分享当前页面

ios_share

https://hci.top/zh/handbook/J5.11.1