ARIA只是辅助技术的“传话工具”,不能替代语义HTML或自动提供交互行为;必须优先使用原生标签,手动补全键盘支持、焦点管理与状态同步,确保语义、行为、样式一致。

这是许多开发者在涉及可访问性时,常会陷入的认知误区。ARIA本身并不会自动提升可访问性,它本质上是一个为辅助技术“递话”的传令兵;如果用错了地方或者滥用,结果可能适得其反,直接破坏可访问性。
ARIA 不是语义 HTML 的替代品
举个例子,不少朋友觉得给一个普通的加上role="button",它就能摇身一变成为功能完备的按钮。事实真的如此吗?很遗憾,并非如此。这个角色属性只是向屏幕阅读器耳语了一句:“这个东西看起来像个按钮。”至于按下空格或回车能不能触发点击、键盘焦点该如何管理、聚焦时该显示什么视觉样式、以及鼠标事件处理——这些原生按钮该有的“本能”,它一概欠奉。最终,这些繁重的交互逻辑,还是得开发者自己动手,一点一点补全。
所以,正确的打开方式应该是:优先使用原生的语义化标签。能用,就别用;能用,就无需。为什么?道理其实很简单:
- 原生元素自带“全家桶”:键盘导航、焦点行为,甚至一些隐含的ARIA语义,浏览器都已经帮你打包好了。
- ARIA无法修复“键盘残疾”:如果一个自定义控件,用户按Tab键根本进不去,或者按下Enter毫无反应,那么无论你给它加上什么ARIA角色,都于事无补。
- 警惕“高级”角色的副作用:像
application这类特殊角色,会直接让屏幕阅读器退出常规的文档浏览模式,反而可能增加用户的认知负担和使用门槛。
aria-hidden 和 display: none 的区别很关键
这两个属性看似都用来隐藏内容,但目标“受众”截然不同,混用就容易出问题。aria-hidden="true"的作用,是只对辅助技术(如屏幕阅读器)“隐身”,内容在视觉上依旧可见。而display: none或visibility: hidden则更彻底,它们会将元素从渲染树和可访问性树中同时移除——也就是说,既看不见,也“听”不着。
立即学习“前端免费学习笔记(深入)”;
这里有一个典型的错误场景:给一个全局的loading遮罩层加上了aria-hidden="true",却忘了禁用底层页面内容的键盘焦点。结果,屏幕阅读器用户虽然听不到遮罩层的提示,却依然能通过Tab键,将焦点移到被完全遮挡住的按钮上,造成交互混乱。
- 对于模态层,正确的做法是:设置
aria-modal="true"并配合现代浏览器的inert属性,或者手动为底层所有可聚焦元素添加tabindex="-1",将它们移出焦点流。 - 动态内容的显隐:优先考虑使用HTML原生的
hidden属性,它比aria-hidden更可靠,能确保状态同步。 - 注意继承性:
aria-hidden="true"默认会影响其所有子元素,除非某个子元素显式地设置了aria-hidden="false"来“反抗”。
动态内容更新必须配 aria-live
在单页应用大行其道的今天,通过Ja vaScript动态更新页面内容已是家常便饭。但是,你是否想过,当页面上的表单验证提示突然出现,或是一条通知悄然而至时,屏幕阅读器用户如何知晓?如果不主动声明aria-live区域,这些动态变化的内容,大概率会被辅助技术“错过”。
然而,aria-live的使用也需讲究策略,并非所有动态区域都适合“插播通知”:
polite(礼貌):等屏幕阅读器说完当前内容后,再播报新变化。适用于非紧急的友好提示,比如“用户名可用”。assertive(迫切):立即中断当前的朗读,优先播报新信息。只应留给那些真正关键、需要用户立即知晓的反馈,比如“支付失败,请重试”。- 避免“信息轰炸”:不要在每秒都在变化的内容(如实时计时器)上设置
aria-live,否则会导致朗读不断中断,用户体验极差。 - 定位要精准:不要图省事,将
aria-live加在整个或频繁重渲染的大容器上。应该精准地包裹住真正会发生内容变动的节点。
最后再强调一个最容易被忽视的核心原则:ARIA属性本身是“声明式”的,它不附赠任何DOM行为。aria-expanded="true"并不会让你的下拉菜单自动展开,aria-selected="true"也不会让选项自动高亮——所有这些视觉和交互状态,都需要开发者亲手用Ja vaScript去同步和维护。真正的可访问性,绝不是添加几个属性就能交差的“表面功夫”,它要求语义(HTML/ARIA)、交互行为(Ja vaScript)、视觉样式(CSS)三者必须时刻保持高度一致。这才是关键所在。
