工具提示感觉就像是你能遇到的最小的 UI 问题。它们很小而且通常是隐藏的。当有人问如何构建一个时,传统的答案几乎总是使用一些 JavaScript 库。很长一段时间以来,这都是明智的建议。 我也跟着做了。 从表面上看,工具提示很简单。将鼠标悬停或聚焦在某个元素上,显示一个带有一些文本的小框,然后在用户离开时将其隐藏。但一旦你将其交付给真正的用户,优势就开始显现出来。键盘用户按 Tab 键进入触发器,但永远看不到工具提示。屏幕阅读器会宣布两次,或者根本不宣布。当您移动鼠标太快时,工具提示会闪烁。它与较小屏幕上的内容重叠。按 Esc 不会将其关闭。焦点失去了。 随着时间的推移,我的工具提示代码变成了我不想再拥有的东西。事件侦听器堆积起来。悬停和焦点必须分开处理。外部点击需要特殊情况。 ARIA 属性必须手动保持同步。每个小修正都增加了另一层逻辑。 图书馆有所帮助,但它们也更像是我工作的黑匣子,而不是完全了解幕后发生的事情。 这就是促使我研究更新的 Popover API 的原因。我想看看如果我在没有库的帮助下使用浏览器的本机模型重建单个工具提示会发生什么。 在我们开始时,值得注意的是,与任何新功能一样,有一些问题仍在解决中。也就是说,尽管整个 API 有几个部分在不断变化,但它目前享有良好的浏览器支持。与此同时,值得关注 Caniuse。 “旧”工具提示 在 Popover API 出现之前,使用工具提示库并不是捷径。这是默认的。浏览器没有跨鼠标、键盘和辅助技术运行的工具提示的原生概念。如果您关心正确性,那么您唯一的选择就是使用库,而这正是我所做的。 在较高的层次上,模式始终是相同的:一个触发器元素、一个隐藏的工具提示元素以及协调两者的 JavaScript。
<按钮类=“信息”>?按钮>
该库处理了允许元素在悬停或聚焦时显示、在模糊或鼠标离开时隐藏以及在滚动时重新定位/调整大小的连接。
随着时间的推移,工具提示可能会变得脆弱。微小的改变也会带来风险。小修复导致回归。更糟糕的是,添加新的工具提示继承了相同的复杂性。从技术上来说事情是可行的,但从来没有感觉已经解决或完成。 这就是当我决定使用浏览器的原生 Popover API 重建工具提示时的情况。 我尝试 Popover API 的那一刻 我没有改用 Popover API,因为我想尝试一些新东西。我切换是因为我厌倦了维护工具提示行为,我相信浏览器应该已经理解了。 一开始我很怀疑。大多数新的 Web API 都承诺简单性,但仍然需要粘合、边缘情况处理或回退逻辑,以悄悄地重新创建您试图逃避的相同复杂性。 因此,我以尽可能最小的方式尝试了 Popover API。看起来是这样的:
<按钮popovertarget =“tip-1”>?按钮>
1.键盘“正常工作” 键盘支持取决于正确排列的多个层:焦点必须触发工具提示,模糊必须隐藏它,Esc 必须手动连接,并且时机很重要。如果您错过了一种边缘情况,工具提示要么保持打开状态太长时间,要么在阅读之前消失。 将 popover 属性设置为自动或手动时,浏览器将接管基础功能:Tab 和 Shift+Tab 正常运行,Esc 每次都会关闭工具提示,并且不需要额外的侦听器。
从我的代码库中消失的是全局按键处理程序、特定于 Esc 的清理逻辑以及键盘导航期间的状态检查。键盘体验不再是我必须维护的东西,它变成了浏览器的保证。 2. 屏幕阅读器的可预测性 这是最大的进步。正如我之前概述的那样,即使 ARIA 工作很仔细,行为也会有所不同。每一个微小的改变都让人感觉有风险。使用具有适当角色的弹出窗口看起来和感觉更加稳定和可预测,就将发生的事情而言:
这是另一个胜利:切换后,Lighthouse 不再标记交互的错误 ARIA 状态警告,这主要是因为不再有自定义 ARIA 状态让我意外出错。
3. 焦点管理 焦点曾经是脆弱的。之前,我有这样的规则:让焦点触发器显示工具提示,将焦点移到工具提示中并且不关闭,当触发器太近时模糊触发器,以及关闭工具提示并手动恢复焦点。这一直有效,直到没有效果为止。 通过 Popover API,浏览器强制执行更简单的模型,其中焦点可以更自然地转移到弹出窗口中。关闭弹出窗口会将焦点返回到触发器,并且不会出现看不见的焦点陷阱或失去焦点的时刻。而且我没有添加焦点恢复代码;我把它去掉了。
结论 Popover API 意味着工具提示不再是您模拟的东西。它们是浏览器可以理解的东西。打开、关闭、键盘行为、转义处理和大部分可访问性现在都来自平台本身,而不是来自临时 JavaScript。 这并不意味着工具提示库已经过时,因为它们对于复杂的设计系统、大量定制或遗留约束仍然有意义,但默认值已经发生了变化。第一次,最简单的工具提示也可能是最正确的。如果您好奇,请尝试这个实验:只需用 Popover API 替换产品中的一个工具提示,不要重写所有内容,不要迁移整个系统,只需选择一个,看看代码中会消失什么。 当平台为您提供更好的原语时,您的胜利不仅是减少了 JavaScript 行数,而且还减少了您需要担心的事情。 查看我的 GitHub 存储库中的完整源代码。 进一步阅读 要更深入地了解弹出窗口和相关 API:
杰夫·格雷厄姆《Poppin' In》 “澄清弹出窗口和对话框之间的关系”,Zell Liew “什么是popover=提示?”,Una Kravets “调用者命令”,丹尼尔·施瓦茨 “使用 HTML 弹出窗口创建自动关闭通知”,Preethi 打开 UI Popover API 解释器 “弹出气球”,约翰·雷亚 “CSS 锚点定位”,胡安·迭戈·罗德里格斯
MDN 还提供了 Popover API 的全面技术文档。