HoverCard 悬停卡片
指针停在触发元素(链接、头像、用户名)上一会儿,在旁边弹出一张预览卡片。 只有一句话的说明用 Tooltip;卡片里要放按钮、表单,或者需要点击才打开的,用 Popover。
触发元素放进 trigger 插槽,指针停 700ms 打开卡片;移开 300ms 后关闭,这期间移进卡片就不关。键盘 Tab 到触发元素上同样打开。
<pt-hover-card>
<a slot="trigger" href="#ptengine">@ptengine</a>
<div style="display: flex; gap: 12px">
<pt-avatar src="data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 viewBox=%270 0 80 80%27%3E%3Crect width=%2780%27 height=%2780%27 fill=%27%238fb3d9%27/%3E%3Ccircle cx=%2740%27 cy=%2731%27 r=%2715%27 fill=%27%23f1d3bd%27/%3E%3Cpath d=%27M12 80c2-18 14-28 28-28s26 10 28 28z%27 fill=%27%232f4b6e%27/%3E%3C/svg%3E" alt="Ptengine">P</pt-avatar>
<div style="display: grid; gap: 4px">
<strong>@ptengine</strong>
<span>热图、A/B 测试与个性化,一站式体验优化。</span>
</div>
</div>
</pt-hover-card>open-delay / close-delay 调整开关的延时(毫秒);placement / align 与 Popover 相同,放不下时翻到对面。
<pt-hover-card open-delay="200" close-delay="1000" placement="top" align="start">
<a slot="trigger" href="#fast">很快打开,停留 1 秒</a>
<div>open-delay=200、close-delay=1000,在触发元素上方行首对齐。</div>
</pt-hover-card>只放补充信息,不放必需操作
悬停卡片是给看得见指针的用户的视觉增强(与 Radix HoverCard 同一语义):
- 它不是对话框,触发元素上不写
aria-expanded,读屏用户拿不到卡片里的内容; - 触屏没有悬停,也没有等价的打开方式(组件忽略触屏的指针进出,点一下不会弹卡片)。
卡片里的信息在别处(例如点开触发链接后的详情页)必须同样拿得到。需要所有人都能操作的内容用 Popover。
打开与关闭
| 操作 | 结果 |
|---|---|
| 指针进入触发元素 | 等 open-delay(默认 700ms)后打开 |
| 指针离开触发元素与卡片 | 等 close-delay(默认 300ms)后关闭 |
| 关闭计时中指针移进卡片 | 取消关闭;离开卡片后重新计时 |
键盘 Tab 聚焦触发元素(:focus-visible) | 等 open-delay 后打开 |
| 焦点离开触发元素与卡片 | 立即关闭 |
| Esc / 在触发元素与卡片之外按下指针 | 立即关闭 |
鼠标点击触发元素带来的焦点不算键盘聚焦,不会打开卡片 —— 点一下链接只是去链接那里。 焦点从触发元素 Tab 进卡片里的链接时卡片保持打开;这时按 Esc 关闭,焦点回到触发元素。
位置
placement(默认 bottom)、align(默认 center)、offset(默认 4px)与 Popover 相同: 放不下时翻到对面,入场动画随之从触发元素一侧滑入。卡片经 popover API 进顶层, 放在 overflow: hidden 的容器、表格单元格或带 transform 的祖先里都不会被裁切。
窄屏(≤640px)下不切底部抽屉:触屏本来打不开它,窄窗口的桌面浏览器里仍贴着触发元素更合理。
宽度默认 256px,用 CSS 变量 --pt-hover-card-width 改。
事件
ptOpen:卡片每次真正渲染出来之后发,不论是交互打开还是外部写open打开的。ptClose:只在用户交互关闭时发,detail.reason是pointer(指针离开)、blur(焦点离开)、escape或outside。外部把open写成false不发。
外部写 open 立即开关,不走延时。与 Modal、Popover 共用一个层栈:Modal 里打开的悬停卡片, 第一下 Esc 只关卡片。
API
属性
| 属性 | Attribute | 类型 | 默认值 | 说明 |
|---|---|---|---|---|
align | align | "center" | "end" | "start" | 'center' | 交叉轴上的对齐:top / bottom 时 start 是行首一侧(RTL 下是右),left / right 时 start 是上 |
closeDelay | close-delay | number | 300 | 指针离开触发元素与卡片后多久关闭(毫秒);这期间移回来就不关 |
offset | offset | number | 4 | 卡片与触发元素之间的距离(px) |
open | open | boolean | false | 是否展开。悬停 / 聚焦打开、离开 / 失焦 / Esc / 点外面收起时组件自己改写;外部写入立即生效,不走延时 |
openDelay | open-delay | number | 700 | 指针进入(或键盘聚焦)触发元素后多久打开(毫秒) |
placement | placement | "bottom" | "left" | "right" | "top" | 'bottom' | 相对触发元素的方位。视口放不下时翻到对面,反射的属性仍是这里的首选值 |
事件
| 事件 | detail 类型 | 说明 |
|---|---|---|
ptClose | { reason: PtHoverCardCloseReason; } | 用户交互收起后发,detail 说明是怎么收起的。外部把 open 写成 false 不发 **不冒泡**:弹层常互相嵌套(弹窗里的菜单、气泡卡片里的下拉),冒泡的话外层上的监听会收到内层的开关。 |
ptOpen | void | 卡片渲染出来之后发(交互或属性打开都发) **不冒泡**:弹层常互相嵌套(弹窗里的菜单、气泡卡片里的下拉),冒泡的话外层上的监听会收到内层的开关。 |
插槽
| 名称 | 说明 |
|---|---|
(默认) | 卡片内容 |
trigger | 触发元素,指针悬停或键盘聚焦时打开卡片 |
可定制的内部元素(::part())
| part | 说明 |
|---|---|
base | 卡片面板(与 panel 同一元素) |
panel | 卡片面板 |
CSS 变量
| 变量 | 说明 |
|---|---|
--pt-hover-card-width | 卡片宽度,默认 256px |