搞网站的时候经常碰到那种小图标,鼠标挪上去就蹦出一行字告诉你这是干啥的,这种小气泡就是工具提示(Tooltip)。很多老铁做的时候直接甩一个title属性就完事,结果屏幕阅读器压根不鸟它,触屏设备直接翻车。这篇东西就掰扯掰扯工具提示的正确打开方式,包括两种不同的使用场景、对应的代码姿势,还有那些容易踩坑的细节。
啥是工具提示
工具提示说白了就是给界面上的控件配一个简短的文字小纸条。它不是弹窗那种霸道总裁式的拦截,而是安安静静躲在角落,只有鼠标悬停或者键盘聚焦到那个控件的时候才冒出来。举个栗子,像Stripe网页右上角那个小铃铛图标,鼠标挪过去就飘出一句“通知”,这就是典型的工具提示。这玩意儿只放纯文字,绝对不能往里塞按钮、链接这些能交互的东西,否则就变味了,那得用<dialog>弹窗来搞。
两种常见场景
工具提示在实战中基本只干两件事:要么给图标起个名字(标签),要么给图标补充一句说明(描述)。这两者用的ARIA属性不一样,搞混了屏幕阅读器的朗读顺序就乱套了。
| 场景类型 | 核心作用 | 推荐属性 |
|---|---|---|
| 标签型 | 告诉这是啥 | aria-labelledby |
| 描述型 | 补充说明 | aria-describedby |
方案一 做标签
有些图标光秃秃的没有文字,比如一个齿轮图标代表“设置”,一个心形图标代表“收藏”。这种情况下工具提示的作用就是给这个图标提供一个可见的名字。正确的做法是用aria-labelledby把按钮和工具提示串起来。
操作步骤:
- 在按钮内部先放好图标(比如用SVG或者字体图标)。
- 在按钮下方或旁边写一个
<div>,加上role="tooltip"属性和一个唯一的id。 - 给按钮加上
aria-labelledby属性,属性值填上那个工具提示的id。 - 如果需要显示动态数字(比如未读消息条数),可以在按钮里再放一个带
id的<span>,然后aria-labelledby里用空格隔开多个id,朗读顺序按从左到右。
代码示范:
<!-- 标签型工具提示:小铃铛图标的标签是“通知” -->
<button aria-labelledby="bellLabel">
<!-- 这里是铃铛图标,实际项目中换成svg或字体 -->
🔔
</button>
<div role="tooltip" id="bellLabel">
通知
</div>
<!-- 带数字的例子:朗读“3 通知” -->
<button aria-labelledby="msgCount msgLabel">
🔔
<span id="msgCount">3</span>
</button>
<div role="tooltip" id="msgLabel">
未读消息
</div>写这段代码的时候有个坑要留意:aria-labelledby引用的元素在页面上可以是隐藏的(比如工具提示默认不可见),但DOM结构里必须存在。另外这个属性会覆盖按钮本身可能有的文字内容,所以如果按钮里本来写了文字,那些文字就不会被读出来了。很多新手在这翻车,明明按钮里有“关闭”两个字,加了aria-labelledby后屏幕阅读器反而读不出“关闭”了。
方案二 做描述
有的图标本身已经有名字了(比如按钮上写了“消息”二字),但是还想再加一句补充说明,像“查看并管理通知设置”。这时候工具提示扮演的是辅助描述的角色,需要用aria-describedby。而且图标本身还得有一个可访问的名字,通常的做法是放一段视觉上隐藏但屏幕阅读器能读到的文字。
操作步骤:
- 按钮里除了图标之外,加一个
<span>包着描述性的文字,并给这个<span>加上class="visually-hidden"(视觉隐藏类)。这类CSS的写法是position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); border: 0;。 - 写工具提示的
<div>,带上role="tooltip"和唯一id。 - 给按钮加上
aria-describedby,属性值填工具提示的id。 - 屏幕阅读器的朗读顺序:先读按钮里的可见文字(包括视觉隐藏的那个名字),然后停顿一下,再读
aria-describedby指向的内容。
代码示范:
<button class="notifications" aria-describedby="notiDetail">
<!-- 铃铛图标 -->
🔔
<span id="msgNum">3</span>
<span class="visually-hidden">通知</span>
</button>
<div role="tooltip" id="notiDetail">
查看并管理通知的各项设置
</div>这里要特别留意:aria-describedby指向的内容不要跟按钮原有的名字重复,否则屏幕阅读器会念两遍差不多的东西,听起来很傻。另外aria-describedby支持多个id,但建议只用一个,因为多个的话某些读屏软件处理得不太流畅。
触屏翻车换这个
工具提示在触摸屏上基本是废的,因为没鼠标悬停,而且手指点一下按钮就会触发点击事件,根本没机会看到那个小气泡。碰到移动端或者需要显示较多内容(甚至带按钮)的提示场景,别硬撑,换用切换提示(Toggletip)。这玩意儿长成一个小圆圈里面带个“i”字母的样子,点一下蹦出一段话,再点一下或者点外面就关闭。
实现方法:
- 做一个
<span>作为容器,里面放一个<button>,按钮的文本或aria-label写上“更多信息”。 - 再放一个
<span>,加上role="status",里面塞需要展示的提示文本。role="status"是一个实时区域,屏幕阅读器会在它内容变化时自动读出来。 - 用JavaScript控制点击按钮时切换那个提示
<span>的显示和隐藏。
代码示范:
<span class="tog-container">
<button type="button" aria-label="点我查看说明">
ⓘ
</button>
<span role="status" class="tog-content hidden">
这里填上需要解释清楚的补充信息,比如“该设置会在凌晨三点自动同步”。
</span>
</span>
<style>
.hidden {
display: none;
}
.tog-content {
display: block;
/* 其他样式:背景、圆角、阴影等 */
}
</style>
<script>
const btn = document.querySelector('.tog-container button');
const content = document.querySelector('.tog-container [role="status"]');
btn.addEventListener('click', () => {
const isHidden = content.classList.contains('hidden');
if (isHidden) {
content.classList.remove('hidden');
} else {
content.classList.add('hidden');
}
});
// 点页面其他地方关闭的逻辑省略,但实际要加上
</script>写切换提示的时候记得把按钮做成可聚焦的,并且支持Escape键关闭。另外role="status"每次显示都会重新朗读内容,如果用户反复点开,读屏会反复念,可以考虑改用aria-live="polite"来控制频率。
那些年踩过的坑
title属性做出来的假工具提示千万别再用,那个东西键盘完全没法聚焦,而且屏幕阅读器的支持稀烂,长文本还显示不全。aria-haspopup属性也不要跟role="tooltip"一起出现,因为那个属性是给菜单类弹窗用的,混在一起会让辅助技术误以为工具提示里藏着交互内容。最关键的一条:永远不要把重要信息只放在工具提示里。万一哪个旧版屏幕阅读器抽风忽略掉了aria-describedby,这部分信息就彻底消失了。得保证就算工具提示完全不工作,页面的核心功能也不受影响。
