应使用 sr-only 类实现视觉隐藏但保留可访问性,其通过绝对定位、尺寸归零、溢出裁剪等组合样式确保屏幕阅读器可读;禁用 display: none 或 visibility: hidden;响应式解除需重置所有 sr-only 属性;慎用于交互元素,并须经 accessibility tree 和 voiceover 等多端测试验证。

用 sr-only 类隐藏内容但保留在可访问性树中
直接用 display: none 或 visibility: hidden 会把内容从屏幕阅读器中彻底移除,这不是“为屏幕阅读器保留”,而是完全丢弃。真正要的是视觉上不可见、但 DOM 结构完整、能被读屏软件识别的隐藏方式。
最稳妥的做法是复用经过广泛验证的 sr-only 工具类(如 Bootstrap 提供的),它通过绝对定位 + 尺寸归零 + 溢出裁剪 + 透明度绕过视觉渲染,同时避免影响可访问性 API:
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
-
clip: rect()比clip-path兼容性更好(IE9+ 支持),且不会触发某些读屏器的渲染异常 - 不能只靠
opacity: 0—— 它仍占布局空间,且部分旧版 NVDA 会跳过完全透明元素 - 避免用
font-size: 0配合line-height: 0—— 某些语音浏览器会因文本尺寸过小而忽略整块内容
响应式场景下需解除隐藏的常见时机
当内容在特定断点需要“从仅屏幕阅读器可见,变为可视”时,不能只写一次 sr-only,必须配合媒体查询动态撤销隐藏规则。
例如:一个表格的列标题在桌面端显示为表头单元格,在移动端折叠为每行前缀文字(仅对读屏用户有意义),但当屏幕变宽后,这个前缀应消失、让真实表头接管:
@media (min-width: 768px) {
.sr-only-table-label {
position: static;
width: auto;
height: auto;
padding: 0;
margin: 0;
overflow: visible;
clip: auto;
white-space: normal;
}
}
- 别用
display: block简单覆盖 —— 它无法恢复原始语义流,可能破坏表格结构或列表嵌套关系 - 撤销时必须重置所有
sr-only中设置的属性,尤其是clip和position,否则残留样式会导致错位或内容不可见 - 如果该元素本身是
<span></span>包裹在<th> 内,解除隐藏后要确认它不干扰表头的 <code>scope属性或 ARIA 关联用
aria-hidden和aria-label替代隐藏内容的边界情况不是所有“只为屏幕阅读器保留”的内容都适合用视觉隐藏类。比如纯图标按钮、装饰性 SVG、重复性分隔线,它们本就不该出现在可访问性树里 —— 这时候反而是
aria-hidden="true"更准确。而真正需要“仅读屏可见”的文案,有时用属性比包裹元素更轻量:
- 按钮内无文本?优先加
aria-label="删除此项",而不是塞个<span class="sr-only">删除此项</span>
- 图表旁有冗余说明?考虑用
aria-describedby="chart-desc" 关联到页面另一处的 <code><div id="chart-desc" class="sr-only">,而非重复放置 <li> <code>aria-live区域的内容更新,不需要任何视觉隐藏 —— 它本身就是专为读屏器设计的通道,强行套sr-only反而可能干扰播报节奏 - 用 Chrome DevTools 的 Accessibility 面板检查该元素是否仍在“Accessibility Tree”中,且
name属性正确(不是空字符串或“undefined”) - 在 macOS 上用 VoiceOver + Safari 测试,按
Ctrl + Option + U打开 rotor,切换到“Links”或“All”视图,确认该内容可被定位和朗读 - 如果元素是动态插入的(如 AJAX 加载后 append),确保它插入时没有被父容器的
aria-hidden="true"意外屏蔽 - 不要依赖“看起来没显示”来判断 —— 某些高对比度模式下,
clip裁剪失效,1px 元素会意外露出,建议在 Windows 高对比度主题下快速扫一眼
测试时容易漏掉的关键检查点
写了
sr-only不等于可访问性就到位了。很多团队上线后才发现问题出在细节上:最麻烦的不是写错类名,而是把
sr-only套在了带交互行为的元素上(比如一个隐藏的<button></button>),结果键盘焦点能进却看不见,又没提供视觉反馈 —— 这种“半隐藏”状态最难调试。 - 按钮内无文本?优先加











