用 .sr-only 类是唯一靠谱的视觉隐藏方案,它通过 position: absolute、width: 1px、height: 1px、clip: rect(0,0,0,0) 等组合确保元素视觉隐藏但保留在可访问性树中;display: none、visibility: hidden 和 aria-hidden="true" 均会彻底移除屏幕阅读器可读性。

直接结论:用 .sr-only 类,而不是 display: none、visibility: hidden 或 aria-hidden="true" —— 后三者都会让屏幕阅读器彻底读不到内容。
为什么 .sr-only 是唯一靠谱的视觉隐藏方案
它不是“藏起来就行”,而是通过一组协同 CSS 属性,确保元素:仍在 DOM 中、保留在可访问性树里、不占布局空间、对 sighted 用户不可见、不意外获得焦点。关键点在于:position: absolute 把它移出流,width: 1px; height: 1px 防止被某些读屏器忽略(零尺寸会被跳过),clip: rect(0, 0, 0, 0) 彻底裁掉渲染区域,white-space: nowrap 避免长文本撑开微小尺寸。
常见翻车点:
- 复制网上的
.sr-only片段但没加border: 0,某些 NVDA + Firefox 组合会误读边框语义 - 父容器设了
overflow: hidden,而.sr-only元素靠右/下定位,结果被裁掉——读屏器读不到 - 在 React/Vue 中用
v-show或useEffect动态加类,导致首次渲染时文本闪现或漏读
aria-label 和 .sr-only 别混着用
它们解决的是不同问题:aria-label 是“替换”控件的可访问名称;.sr-only 是“补充”额外上下文。混用会导致重复播报。
正确做法:
- 图标按钮缺文字?用
<button aria-label="删除"></button>,别套<span class="sr-only">删除</span> - 需要说明“仅限会员”或“已过期”这类附加信息?用
<span class="sr-only">仅限会员</span>,放在相关元素旁 - 表单
<label></label>里嵌.sr-only文本是安全的,因为<label></label>本身是语义容器
动态场景下怎么避免 hydration 或 SSR 失步
服务端渲染时 CSS 还没加载,.sr-only 元素会短暂显示;客户端 JS 注入后又突然消失——不仅影响 sighted 用户体验,还可能触发屏幕阅读器重复朗读。
稳妥做法:
- 把
.sr-only样式内联进(critical CSS),确保首屏即生效 - React 中默认带
sr-only类,逻辑控制是否渲染该元素本身,而非靠 JS 切换类名 - Vue 中禁用
v-show包裹.sr-only,改用v-if或纯 CSS 控制显隐 - 若必须 JS 控制,用
el.hidden = true(HTML5hidden属性)兜底,它比 class 更早介入渲染流程
真正难的不是写出那几行 CSS,而是每次加隐藏文本前,都得问一句:这个信息是否对键盘用户、触控用户、屏幕阅读器用户都一致可达?漏掉任一路径,就不是“优雅隐藏”,而是“悄悄失效”。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











