工具提示需要延迟,但随后需要跳过它
摘要
文章描述了一种工具提示的UX设计模式:首次悬停时添加延迟,但在响应窗口内允许即时提示,从而在多个交互元素之间移动时提升用户体验。
暂无内容
查看缓存全文
缓存时间: 2026/08/25 19:59
# 工具提示需要延迟,然后需要跳过延迟
来源:https://blog.master.dev/tooltips-need-a-delay-and-then-they-need-to-skip-it/
在我的产品FrontPrep (https://www.frontprep.com/user-interface)的问题列表页面上,我在面试问题旁边显示公司标志。当您将鼠标悬停在标志上时,工具提示会显示公司名称。
有一件事困扰了我好几天。如果我只是移动光标穿过页面,工具提示会沿途持续立即显示。原因很简单:我将工具提示延迟设置为`0`,因此光标悬停在标志上时立即出现。
为了解决这个问题,我添加了200毫秒的过渡延迟,这有效但产生了一个新问题。如果您查看用户界面,有几行中多个标志并排显示,因为相同的面试问题在多家公司被提问。现在,从一个标志移动到另一个标志意味着每次悬停在不同的公司标志上时都要等待相同的200毫秒延迟,这感觉很迟钝并导致用户体验不佳。
这篇文章不是关于构建工具提示。它是关于一个小型交互模式,您可以在浏览器工具栏和各种网站中找到相同的模式,而人们并未意识到它。
这是我网站上前后的体验短视频。它帮助您更好地理解问题。
之前(无200毫秒延迟)之后(有200毫秒延迟和即时工具提示)## 解决方案
我将我的解决方案分为三个部分。
1. 您悬停在一个标志上。工具提示等待200毫秒后打开。
2. 工具提示关闭,300毫秒计时器开始。我称这为温暖窗口或温暖页面。如果在运行这300毫秒计时器时您悬停在另一个标志上,或者当页面温暖时,工具提示会立即打开,无需任何等待或动画。
3. 当300毫秒计时器到期时,一切恢复正常,页面变冷。如果您再次悬停在标志上,下一个工具提示必须再次等待200毫秒。没有这一步,您打开的第一个工具提示将永久关闭整个页面的延迟。
``
悬停 → 等待200毫秒 → 工具提示打开(页面现在温暖)
离开 → 工具提示关闭 → 300毫秒冷却
├─ 在冷却前悬停另一个工具提示 → 立即打开,页面保持温暖
└─ 冷却结束 → 页面寒冷,200毫秒等待恢复
``
在FrontPrep中,我的工具提示使用Radix和Motion构建。我将使用下面一个更简单的React版本来演示这种模式,并且在文章后面,我将把这个想法放入一个Claude技能中,您可以使用它来审计您自己的代码库。
这里有一篇后续文章(https://blog.master.dev/delayed-then-instant-tooltips-with-html-css-alone/),仅使用HTML和CSS复制了此处的基本用户体验,无需任何JavaScript。
## 代码如何工作
我将尝试按照移动光标时事件发生的顺序来解释代码。
### 第1步:您悬停在标志上
当在标志上触发`onMouseEnter`时,调用此函数:
``
function handleEnter() {
// `tooltips`是一个useContext变量,用于在组件之间跟踪isWarm状态
if (tooltips.isWarm) {
show();
return;
}
// `openTimer`是一个useRef变量,所以.current是设置/访问值的方式。
openTimer.current = setTimeout(show, tooltips.openDelay);
}Code language: JavaScript (javascript)
``
悬停时,此组件会问一个问题:页面是否温暖?如果是,工具提示立即打开。如果不是,它开始一个200毫秒的计时器并等待。您可能想知道这个tooltips对象来自哪里。它来自`TooltipProvider`,我将在最后一步讨论它。
### 第2步:工具提示打开
这是处理工具提示打开的show函数:
``
function show() {
setInstant(tooltips.isWarm);
setOpen(true);
tooltips.markOpened();
}Code language: JavaScript (javascript)
``
这做了三件事:
1. 它将`tooltips.isWarm`的值复制到一个名为`instant`的状态中。
2. 它打开工具提示。
3. 它告诉提供者工具提示已打开,这使页面变温暖。
回到第1点,它将`tooltips.isWarm`返回的内容复制到一个名为instant的标志中,因为这个instant标志作为数据属性添加到工具提示中,CSS使用它来跳过进入动画。
``
.tooltip[data-instant="true"] {
transition-duration: 0ms;
}Code language: CSS (css)
``
这就是为什么当页面温暖时,工具提示没有任何动画或延迟地打开。
### 第3步:您离开标志
当在标志上触发`onMouseLeave`时,调用此函数:
``
function handleLeave() {
clearTimeout(openTimer.current);
if (!open) return;
setOpen(false);
setInstant(false);
tooltips.markClosed();
}Code language: JavaScript (javascript)
``
首先,我们需要知道工具提示当前是否打开。
假设您的光标在50毫秒内穿过一个标志,这远远少于200毫秒,因此`handleEnter`启动的计时器仍在运行且工具提示尚未打开。`handleLeave`中的`clearTimeout`取消该计时器,因此工具提示永远不会打开,并且`if (!open) return;`在此处停止函数,因为从未打开的工具提示无需关闭,也无需启动冷却。在第一个视频中,一次扫过会打开沿途的每个工具提示;现在,相同的扫过不会打开任何工具提示。
如果工具提示已打开,当您将光标停留在标志上超过200毫秒,或者当页面温暖时,工具提示在进入时立即打开时会发生这种情况。我们将关闭工具提示并告诉我们的提供者,然后提供者将启动一个300毫秒的冷却计时器,如果您在此计时器结束前悬停在下一个标志上,工具提示会立即打开。
### 第4步:提供者
``
const warm = useRef(false);
const cooldownTimer = useRef(null);
useEffect(() => () => clearTimeout(cooldownTimer.current), []);
const tooltips = useMemo(
() => ({
openDelay,
isWarm: () => skipWhenWarm && warm.current,
markOpened() {
warm.current = true;
clearTimeout(cooldownTimer.current);
},
markClosed() {
clearTimeout(cooldownTimer.current);
cooldownTimer.current = setTimeout(() => {
warm.current = false;
}, warmFor);
},
}),
[openDelay, warmFor, skipWhenWarm]
);Code language: JavaScript (javascript)
``
这是此整个模式工作所需的共享状态。页面可以是温暖的或寒冷的。如果您注意到,markOpened会取消挂起的冷却;这就是当您从一个标志移动到另一个标志时保持页面温暖的原因。基本上,每个新工具提示都会取消前一个工具提示启动的冷却。
您不需要对`skipWhenWarm`标志感到困惑,它仅用于演示中的前后切换;将其关闭总是使提供者寒冷,以帮助您看到之前的行为。
这里另一个重要的细节是`isWarm`是一个ref而不是React状态,因为更改它不会重新渲染页面上的所有工具提示。此ref仅在事件处理程序内部读取。
## 为什么选择200毫秒计时器?
如果我们选择少于150毫秒的计时器,当光标经过工具提示触发器时,它很可能会打开工具提示。如果我们选择超过250毫秒的计时器,悬停将感觉迟钝和损坏。因此,在这种情况下,200毫秒是一个良好、平衡的数字。
## 判断力与品味
如果您要求AI构建工具提示,它将构建一个功能完全的工具提示,但最终,您,人类,将决定构建的工具提示是否值得,因为有些细节能区分一个能用的工具提示和一个精致的工具提示,就像它们区分一个能用的产品和一个精致的产品一样。AI只有在您指导它这样做时才能构建精致的产品,而当您培养出品味和判断力时,您就可以指导它,这些来自多年的经验、错误和实践。
然而,您可以利用其他工程师/设计师的技能来获得他们对精致产品的品味,并最终在您开始形成自己的判断力时构建您的产品,您可以创建/使用关于颜色、可访问性、表单、动画、排版等的技能,您可以生成可靠的输出,但这仍然不意味着它已准备好生产,最终由您来判断,并且只有在满足您的标准和品味时才发布。
## 技能
我为了解决我面临的上述问题创建了一个Claude技能,您可以构建类似的技能或复制下面编写的技能并运行它来审计您的代码库。每个代码库都是不同的,并且每个代码库使用不同的工具提示库或以不同的方式编写了自定义工具提示;此技能将适用于所有。
``
.claude /skills /tooltip /SKILL.md
``
``
---
name: tooltip
description: 工具提示需要延迟,以便它们不会在非故意的鼠标移动时打开
---
# 工具提示时机
点击总是有意的,而悬停则不是有意的。光标穿过页面到达它需要去的地方,并沿途经过元素,因此工具提示无法从悬停中判断用户是否想要打开它。200毫秒是它用来确认的时间。
``css
.tooltip {
transition-delay: 200ms;
}
```
## 三个数字和状态
| 值 | 数字 | 原因 |
| ----------- | ------ | ------------------------------------------------------------------- |
| 打开延迟 | 200毫秒 | 低于150毫秒时,仅经过触发器的光标仍会打开它。高于250毫秒时,有意的悬停会感觉损坏。 |
| 温暖窗口 | 300毫秒 | 足够长以覆盖从一个触发器移动到下一个触发器的时间。足够短,以便一秒钟后的悬停再次等待。 |
| 关闭延迟 | 0毫秒 | 离开触发器应该是明确的。所以没有什么需要等待。|
## 它应该如何表现
``
悬停 -> 等待200毫秒 -> 工具提示打开(页面现在温暖)
离开 -> 工具提示关闭 -> 300毫秒冷却
``Code language: Markdown (markdown)
``
本文表达的观点仅代表作者个人,不代表Coinbase。
相似文章
修复我的工具提示可访问性错误
Jake Archibald 分享了他在使用 popover="hint" 和 aria-describedby 实现工具提示时犯下的可访问性错误,并解释了在收到可访问性专家反馈后的正确模式。
别劫持我的鼠标指针
作者批评了用自定义动画效果替换标准网页光标的趋势,认为这些装饰严重损害了可用性。作者呼吁开发者优先考虑实用的用户体验(UX),而不是现代 AI 辅助编码工具所催生的炫目视觉效果。
Cursor 更新后的 Design Mode(3分钟阅读)
Cursor 更新了其 Design Mode,新增点击、绘制和语音输入等视觉提示,允许直接编辑 UI 元素,从而改善设计师和开发者的工作流程。
跨文档视图转换:无人提及的那些陷阱
一篇技术文章,解释CSS中跨文档视图转换的当前实现,涵盖已弃用的meta标签、常见陷阱(如4秒超时和宽高比变形)以及正确的选择加入方法。
HTML 的新能力
文章重点介绍了新的 HTML 特性,如 <dialog>、弹出框(popovers)和分组 <details>,这些特性无需 JavaScript 即可实现动态功能,展示了 Web 标准和浏览器兼容性的进步。