Popover 气泡卡片

点击/鼠标移入元素,弹出气泡式的卡片浮层。

适用场景:需要一个在触发器悬停或点击时打开的浮动气泡面板时,<r-popover> 会帮你定位、传送(portal)其 <r-content> 面板,并接好无障碍访问支持。

快速开始

基础用法

触发器放在默认插槽中;浮层内容包裹在嵌套的 <r-content> 元素里。

popover
这是浮层内容
<r-popover style="display: inline-block;">
  <r-button>popover</r-button>
  <r-content>
    <div>这是浮层内容</div>
  </r-content>
</r-popover>

API 参考

属性

属性 类型 默认值 说明
placement string 'top' 面板相对触发器的位置:topbottomleftright,每个都可以再加 -start(默认)、-center-end 后缀
trigger string 'hover' 面板打开方式:hoverclickclick 事件始终会绑定)
getPopupContainerId string '' 面板定位所在容器的元素 id(在打开时读取,不会反映为属性)
sheet string '' 注入到组件 Shadow DOM 的 CSS

触发方式 trigger

hover
hover
click
click
<r-popover trigger="hover" style="display: inline-block;">
  <r-button>hover</r-button>
  <r-content>
    <div>hover</div>
  </r-content>
</r-popover>

<r-popover trigger="click" style="display: inline-block;">
  <r-button>click</r-button>
  <r-content>
    <div>click</div>
  </r-content>
</r-popover>

位置 placement

top
top
bottom
bottom
left
left
right
right
<r-popover trigger="hover" placement="top" style="display: inline-block;">
  <r-button>top</r-button>
  <r-content>
    <div>top</div>
  </r-content>
</r-popover>

<r-popover trigger="hover" placement="bottom" style="display: inline-block;">
  <r-button>bottom</r-button>
  <r-content>
    <div>bottom</div>
  </r-content>
</r-popover>

<r-popover trigger="hover" placement="left" style="display: inline-block;">
  <r-button>left</r-button>
  <r-content>
    <div>left</div>
  </r-content>
</r-popover>

<r-popover trigger="hover" placement="right" style="display: inline-block;">
  <r-button>right</r-button>
  <r-content>
    <div>right</div>
  </r-content>
</r-popover>

对齐方式 placement="<方向>-<对齐>"

只写方向时,面板的起始边与触发器的起始边对齐。需要面板在触发器上居中、或与触发器的末尾边对齐时,加 -center-end 后缀。顶栏右端的菜单要的就是后者:它向内展开,而不是先溢出视口、再被平移推回来。后缀会跟着自动翻转一起保留:bottom-end 翻转后是 top-end,而不是 top

bottom
bottom — 等同于 bottom-start
bottom-center
bottom-center
bottom-end
bottom-end
<r-popover trigger="hover" placement="bottom-end" style="display: inline-block;">
  <r-button>bottom-end</r-button>
  <r-content>
    <div style="width: 200px;">bottom-end</div>
  </r-content>
</r-popover>

插槽

组件 插槽 说明
<r-popover> (默认) 触发器元素以及 <r-content> 包裹层
<r-content> (默认) 浮层的内容;这些子节点会被传送(portal)到 document.body,并在打开时显示

两个组件都只暴露一个匿名默认插槽,没有具名插槽。

展开状态 open

open 就是面板的状态,像 <details open><dialog open> 一样反射为属性。没有任何地方再从面板的 display 反推状态(那个值比状态滞后一整段退场动画),所以属性、aria-expanded 和屏幕上看到的三者不会互相矛盾。

<r-popover id="pop" trigger="click">
  <r-button>触发器</r-button>
  <r-content><div>内容</div></r-content>
</r-popover>

<script>
  const pop = document.getElementById('pop');
  pop.open = true; // 或 pop.show()
  pop.open = false; // 或 pop.hide()
  pop.toggle();
</script>

show()hide()toggle() 只是它的薄封装;closePopover() 作为 hide() 的别名保留。

事件

<r-popover> 会在面板开合前后派发四个事件,都不带 detail

事件 时机
show 面板即将出现。
after-show 面板已出现,入场动画(若有)已结束。
hide 面板即将关闭。
after-hide 面板已关闭,退场动画(若有)已结束。

等待的是样式表里那个动画本身,而不是抄进脚本里的一个时长。所以在 prefers-reduced-motion 下(压根没有动画要播),after-hide 会紧接着 hide 发出,而不是干等一个固定延迟。

除此之外,它由标准的 DOM 交互驱动:

  • 打开mouseenter(当 trigger 包含 hover 时)、click,或聚焦时按下 Enter / Space
  • 关闭mouseleave(hover 模式)、按下 Escape,或点击文档中的其他位置。

在内部,配套的 <r-content> 元素会用 MutationObserver 监视自身子树,并派发一个 change CustomEventdetail: { type, value: { content, mutation } }),popover 消费这个事件以保持面板同步。这是一个实现细节,而非公开 API。

无障碍访问是自动接好的:host 元素会带上 tabindex="0"aria-haspopup="dialog",以及会随面板开关在 "false""true" 之间切换的 aria-expanded

最佳实践

  • 触发元素:把可聚焦的控件(例如 <r-button>)作为触发器,这样键盘打开/关闭才能正常工作。
  • 内容包裹:始终把面板内容包裹在 <r-content> 中,不在 <r-content> 里的普通子节点不会作为浮层显示。
  • 内联尺寸:host 元素默认是 display: block;加上 style="display: inline-block;"(或放在内联上下文中)让它收缩到触发器大小。
  • 位置placement 只是一个偏好,而非保证:当触发器靠近视口边缘、首选方向空间不够时,面板会自动翻转到相反一侧,并沿交叉轴平移以保持在可视区域内。这种自动翻转只在默认的 body 级定位下生效。
  • 限定容器:当不想用默认的 body 级定位时,用 getPopupContainerId 把面板锚定到指定的滚动/定位容器内。这种模式下不会应用翻转/平移,因此要选择一个适合该容器的 placement。对齐后缀在这种模式下照常生效,与 body 级定位完全一致。