Skip to content

Popover 气泡卡片

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

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

快速开始

基础用法

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

popover
this is content
xml
<r-popover style="display: inline-block;">
    <r-button>popover</r-button>
    <r-content>
      <div>this is content</div>
    </r-content>
</r-popover>

API 参考

属性

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

触发方式 trigger

触发方式

  • hover
hover
hover
xml
<r-popover trigger="hover" style="display: inline-block;">
    <r-button>hover</r-button>
    <r-content>
      <div>hover</div>
    </r-content>
  </r-popover>
  • click
click
click
xml
<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
top
xml
<r-popover trigger="hover" placement="top" style="display: inline-block;">
    <r-button>top</r-button>
    <r-content>
      <div>top</div>
    </r-content>
  </r-popover>
  • bottom
bottom
bottom
xml
<r-popover trigger="hover" placement="bottom" style="display: inline-block;">
    <r-button>bottom</r-button>
    <r-content>
      <div>bottom</div>
    </r-content>
  </r-popover>
  • left
left
left
xml
<r-popover trigger="hover" placement="left" style="display: inline-block;">
    <r-button>left</r-button>
    <r-content>
      <div>left</div>
    </r-content>
  </r-popover>
  • right
right
right
xml
<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
html
<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 和屏幕上看到的三者不会互相矛盾。

html
<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 级定位完全一致。

Released under the MIT License.