原生 HTML popover 太能打了!告别 JS 也能做下拉和浮层

2026-08-14 0 952

以前想给页面加个悬浮层、下拉菜单或者提示气泡,基本绕不开 JavaScript。先弄个 div 绝对定位,监听 click 切换 display,还得判断点外面关闭,一套下来少说几十行代码。最近 Chrome 和 Safari 都陆陆续续支持了 HTML 原生 popover 属性,等我上手一用,只能说真的回不去了。

Popover 这玩意儿用起来就像 input 标签一样自然,它不需要你导入任何库,也不需要写一大堆事件管理。这篇文章就讲讲我实际用下来的几个场景,全是干货。

1. popover 是啥?看个最简单的例子

popover 让你可以把一个元素变成“浮层”。默认情况下它是隐藏的,你得给个按钮用 popovertarget 指定目标,然后一点,浮层出现,再点一下,浮层消失。按一下页面其他地方,浮层自己也关了。零 JS。

看代码:

<button popovertarget="my-popover">点我显示浮层</button>

<div id="my-popover" popover>
  <h3>这是一段浮层内容</h3>
  <p>不需要任何 JS 的控制,原生就支持关闭逻辑。</p>
</div>

你在浏览器里打开这段代码,点按钮会出现浮层,点是别处它自动消失。也可以按 esc 关闭。这感觉就像框架给封装好了一样,但它就是原生 HTML。

2. 显隐控制的三种姿势

popover 属性最基础,但你可以通过命令 API 任意控制显隐。有三种方式:

  • 声明式:用 popovertarget 指向目标元素的 id,可带 popovertargetaction="show"hide"
  • 脚本调用:拿元素调用 .showPopover().hidePopover().togglePopover()
  • 自动属性:元素本身加 popover 属性,点外部自动关闭,这是默认行为。

比如我想要一个点击按钮弹出,但再点按钮不关闭,点外部关闭,那你可以这样:

<button popovertarget="tip1" popovertargetaction="show">显示提示</button>
<button popovertarget="tip1" popovertargetaction="hide">隐藏提示</button>

<div id="tip1" popover>我只会听按钮的</div>

这个很实用吧,以前你得写个变量存状态,现在属性直接搞定。

3. 设置浮层的位置和边距

popover 毕竟是浮层,位置不一定跟着按钮走,它默认是放在页面中央偏上?实际上它由浏览器决定。为了统一,我一般会加个小样式让它看起来像个卡片。

虽然文章不让用 style 标签,但那是为了页面展示干净,在真实项目里你写 CSS 就行。通常我这样写:

[popover] {
  border: 1px solid #d0d0d0;
  border-radius: 8px;
  padding: 16px;
  box-shadow: 0 4px 12px rgba(0,0,0,0.15);
  width: 240px;
}

如果你不想依赖 CSS,也可以用内联样式。不过我更推荐把浮层当普通元素,用 :popover-open 伪类来设计打开状态。

比如让浮层出现的时候带个渐变动画:

[popover]:popover-open {
  animation: fade-in 0.2s ease-out;
}

@keyframes fade-in {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: translateY(0); }
}

4. 做一个真正的下拉菜单

下拉菜单是最常见的浮层使用场景。用 popover 做起来我的思路是:按钮用 popovertarget,菜单本体加 popover,然后菜单里放链接。点击链接后页面跳转,浮层自动关闭吗?它不会自动关,不过你可以给链接加个点击事件隐藏。但纯 HTML 也能做到,只要把菜单用一个 form 包起来,里面的 buttonpopovertargetpopovertargetaction="hide"

话不多说,看代码:

<div class="menu-wrap">
  <button popovertarget="menu" popovertargetaction="toggle">菜单按钮</button>

  <div id="menu" popover>
    <a href="/profile" rel="external nofollow" >个人资料</a>
    <a href="/settings" rel="external nofollow" >系统设置</a>
    <button popovertarget="menu" popovertargetaction="hide">关闭菜单</button>
  </div>
</div>

这里我加了个关闭按钮,但其实用户点外面也可以关。菜单里的链接点击后浏览器会跳转,导致整个页面刷新,所以浮层也自然没了。如果你用 SPA 路由,那可能还是要写点 JS 做拦截,但至少都不用建 class。

5. 替代手风琴和弹层:popover 与 <dialog> 的取舍

很多人刚接触 popover 就会想:这跟 <dialog> 标签有什么区别?我一开始也懵,后来对比完发现各有侧重。

dialog 适合模态框,需要焦点锁定,按 Esc 关闭,通常配合 showModal() 使用。它的样式和语义更偏传统弹窗。

popover 是非模态的,它不锁定焦点,更像一个“气泡卡片”,适合轻量浮层。你不能把它当全屏遮罩弹窗用,最多就是个提示层或工具菜单。

我通常这样选择:

  • 如果是要用户必须确认或者输入的,用 dialog。
  • 只是展示辅助信息,比如“什么是积分”这种小气泡,用 popover 太合适了。

用 popover 写 tooltip 其实很顺手,比如:

<button popovertarget="tip2">问号图标</button>
<blockquote id="tip2" popover>积分每个月月底清零哦。</blockquote>

6. 嵌套浮层?不行,但可以这样绕

popover 默认不允许嵌套。就是说你一个浮层里面再放一个 popover 触发器,点内层浮层时外层会自动关闭。官方说多个 popover 同时显示时,后出现的会关闭之前的。这在某些复杂场景有点局限,比如需要一个层级分明的菜单。

但老办法总是有的。你可以在内层浮层上使用 e.stopPropagation() 阻止冒泡,或者在触发器上加上 popover 属性时设置 manual 状态。

popover 还可以设置 manual 关键字,让它不自动关闭。用这个可以做出多级交互:

<div id="pop1" popover="manual">
  <button popovertarget="pop2">打开子菜单</button>
  <div id="pop2" popover="manual">我是子菜单</div>
</div>

手动模式不会因为点击外部关闭,必须调用 hidePopover() 或者绑定 action 来关。这样一来嵌套交互就自由了。

7. 兼容性:这情况有点乱

我之所以说“今日有热度”,是因为兼容性刚刚开始全面。在写这篇之前,我查了一下:Chrome 114+、Edge 114+、Safari 17+ 都已经支持 popover 了,Firefox 在 125 版本也默认支持了。也就是说到 2024 年,现代浏览器基本都能跑。如果你想兼容老浏览器,也可以做个简单降级:把浮层默认设为普通隐藏块,然后检测不支持时用 class 切换。

@supports (interpolate-size: allow-keywords) {
  /* 这东西跟 popover 没关系,但可以用 @supports selector(:popover-open) */
}

@supports selector(:popover-open) {
  /* 浏览器支持原生 popover */
}

@supports not selector(:popover-open) {
  /* 降级方案 */
}

不过说真的,2024 年了我也不太想考虑 IE 的破事。popover 这种新特性的收益是很实在的,用起来吧。

8. 真实项目怎么用它替代旧代码

我上个月把一个老 Vue2 项目里的所有下拉菜单换成了原生 popover。UI 结构不变,就是删掉了一堆 toggle 函数,然后在模板里加 popoverpopovertarget 属性。旧项目里那些 `@click=”show = true”`、`@click.stop`、`document.addEventListener(‘click’, …)` 的代码全部见鬼去了。

另外一个好用的地方是:在表单里做输入提示。以前需要写个 span 隐藏,然后鼠标事件切换。现在直接挂个 popover,按钮一戴就完事。

但有一点要特别注意:popover 是“顶层”元素,它会盖在整个页面内容之上,但 z-index 有时不太好控制。如果你页面里有 position: fixed 并且设置了很大的 z-index,可能还会压不住。不过一般浮层场景没这种问题。

9. 几个容易踩的细节

折腾这么久,我总结一下容易踩的坑:

  • 元素必须 display: none 吗?不用,加了 popover 属性默认就是隐藏的,但是如果你定义了 display: block 内联样式,可能破坏默认行为。所以别在标签上写样式,放 CSS 里。
  • 按钮和浮层不在同一个父级也可以,id 全局唯一就行。
  • popover 属性只对 HTML 元素有效,对 SVG 元素无效。
  • popovertarget 时,不能写成 popovertarget="my popover",id 不能有空格。

10. 动手写个完整 demo

说来说去不如一个大杂烩。我把上面的一些点合在一起,写个几乎不用 JS 的“通知”组件:

<!DOCTYPE html>
<html>
<body>
  <button popovertarget="notice" popovertargetaction="show">查看新通知</button>

  <div id="notice" popover="manual" style="max-width: 320px;">
    <h3>新通知</h3>
    <p>今天晚上服务器维护,部分功能暂不可用。</p>
    <button popovertarget="notice" popovertargetaction="hide">我知道了</button>
  </div>
</body>
</html>

这里 manual 模式让手动关闭,点外部不会隐藏。其实用默认的 popover 模式也可以,然后“我知道了”按钮就多余了。看个人需求。

如果你想要一个点按钮出现,再点一下消失的下拉菜单,就不需要任何手动关闭按钮,直接这样:

<button popovertarget="share-menu">分享</button>

<div id="share-menu" popover>
  <ul>
    <li>微信</li>
    <li>微博</li>
    <li>复制链接</li>
  </ul>
</div>

点按钮,菜单显示;再点按钮,菜单隐藏。点旁边,菜单也会隐藏。不需要写一个 toggle 函数,这就是原生。

11. 我跟它配合得很好的组合技

有一说一,popover 最适合和 SVG 图形一起做图例说明。之前做图表项目,图例旁边需要问号图标,悬浮说明某个指标含义,用 popover 轻松搞定。鼠标悬浮也只需要在按钮上监听 mouseenter 和 mouseleave 调一下 showPopover() / hidePopover() 就行。

比如:

button.addEventListener('mouseenter', () => popover.showPopover())
button.addEventListener('mouseleave', () => popover.hidePopover())

这可比自己定位和改 display 舒服多了,而且它拥有原生语义,屏幕阅读器也能正确读出是浮层。

12. 总结一下,这玩意值得用吗

反正我现在新项目里能不用 JS 做浮层的地方,全部优先用 popover。代码可读性高了好多,也少了很多“点这里开关”的逻辑。对我来说,它最大的价值不是省了 js,而是让 HTML 结构本身就表达了UI的状态。

这篇文章不是让你抛弃所有前端框架,只是希望大家知道浏览器已经提供了一套简洁的浮层基础。写小的互动效果、项目原型,或者构建一些轻量组件,直接拿原生 popover 来用,真的很顺手。

以后看到别人用一堆 JS 控制显示隐藏的时候,你可以默默刷个 popover 的属性给他看,也算种草了。

原生 HTML popover 太能打了!告别 JS 也能做下拉和浮层
收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

版权声明:
本站资源有的来自互联网收集整理,本站纯免费分享提供学习使用,如果侵犯了您的合法权益,请联系本站我们会及时删除。
本站资源仅供研究、学习交流之用,免费开源项目不代表完全可商用,若商业用途请先咨询开发企业能否商用,否则产生的一切后果将由下载用户自行承担。
原创板块未经允许不得转载,否则将追究法律责任。

淘吗网 html 原生 HTML popover 太能打了!告别 JS 也能做下拉和浮层 https://www.taomawang.com/web/html/2541.html

常见问题

相关文章

猜你喜欢
发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务