最近做公司官网的FAQ和消息通知中心,突然发现弹窗、气泡、浮层这种交互,居然可以用纯HTML写出来,一个JavaScript都不用写。一开始我还以为是浏览器没跟上,直到试了试<dialog>和popover属性,真的又简单又顺手。今天就把这俩原生玩意的用法和搭配实战整理出来,全是硬核案例。
一、<dialog>标签:官方钦定的对话框
以前做模态框,基本步骤是:写一个遮罩Div,内容Div,然后用JS控制显示隐藏,还得处理esc关闭和点击遮罩关闭。现在浏览器内置了<dialog>,自带顶层层叠、焦点管理、按Esc关闭这些特性,实在感动。
基础用法一:非模态弹窗
直接给dialog加open属性,页面加载就显示。但这种弹窗不阻止用户操作背后的内容。
<dialog open>
<p>我是一个朴素的原生弹窗</p>
</dialog>
基础用法二:模态弹窗
真正的模态框要调用showModal()方法,不过我们仍然不需要写样式,只需要在<dialog>上添加一个按钮和form来关闭。看例子:
<dialog id="sureDialog">
<p>确认要删除这条记录吗?</p>
<form method="dialog">
<button value="cancel">取消</button>
<button value="ok">删除</button>
</form>
</dialog>
<button id="showBtn">弹出确认框</button>
<script>
showBtn.addEventListener('click', () => sureDialog.showModal());
</script>
注意,form method="dialog"是HTML内置的关闭dialog的方法。点取消或删除,表单会自动提交并关闭dialog,不需要写关闭函数。如果需要在js里读取点的是哪个按钮,可以监听dialog的close事件,通过dialog.returnValue获取按钮的value。不过既然说不要JS,但显示模态框还是需要一行showModal(),没办法。
二、popover属性:气泡、浮层的亲爹
如果说dialog适合大块弹窗,popover则更适合轻量气泡和下拉浮层。最棒的是,popover完全由HTML声明式触发,连一行JS都不需要。它本质上是一个全局属性,可以加在任何元素上。
基本玩法:popovertarget
要实现点击按钮弹出气泡,只在按钮和气泡上加两个属性就行:
<button popovertarget="tip">点我显示气泡</button>
<div id="tip" popover>
大家好,我是popover气泡
</div>
就这么简单,点击按钮,气泡出现;再点一下按钮,气泡消失。点气泡外部,也会自动关闭。这背后是浏览器替我们做了全部交互。
让popover变成“悬浮确认框”
配合<form method="dialog">可以做出带按钮的气泡确认框,点按钮能关闭,也还能拿到返回值。
<button popovertarget="delPop">删除项目</button>
<div id="delPop" popover>
<strong>确定删除?</strong>
<form method="dialog" popover-target="delPop">
<button value="no">算了</button>
<button value="yes">删!</button>
</form>
</div>
注意,上面form里有个popover-target="delPop",它是专门用来关闭popover的,点按钮就会触发关闭。如果想切换popover状态,就用popovertargetaction="toggle",值还可以是show或hide。
三、dialog + popover 组合:做一个通知中心
看完了两者单独用,是不是觉得很爽?更爽的是它们能组合起来。比如我想做一个页面右下角的通知气泡列表,点按钮弹出popover,popover里面再放一个dialog,用来查看通知详情。
HTML结构
<button popovertarget="notifyPop">🔔 查看通知(3)</button>
<div id="notifyPop" popover class="notify-pop">
<h3>最新消息</h3>
<ul>
<li>
系统升级通知
<button popovertarget="noticeDetail">查看详情</button>
</li>
</ul>
</div>
<div id="noticeDetail" popover class="detail-pop">
<p>系统将于周五凌晨2点升级,升级期间服务不可用。</p>
<form method="dialog" popover-target="noticeDetail">
<button>关闭</button>
</form>
</div>
这样用户点“查看详情”时,详情泡会覆盖在通知泡上面,点关闭后自动回到通知列表。完全没有写JS事件监听,只有纯HTML弹窗交互。这种嵌套popover的方式非常符合现代UI风格。
四、真正好用的:popover配合锚点定位
popover还能和CSS锚点定位结合(CSS anchor positioning),让气泡自动挂在触发按钮旁边,而不是默认的居中。最新Chrome和Edge都能用了。给按钮加一个anchor属性,popover元素用position: absolute; position-anchor:--button之类,不过今天不谈CSS,因为我们不写样式。但原生行为是popover默认显示在它HTML位置的旁边,如果希望气泡紧贴按钮,官方推荐做法是把popover元素直接放在按钮附近,利用绝对定位。如果不用CSS,可能只能默认居中,但是弹出位置的默认值其实也还行,相当于屏幕中间。
说实话,平时开发中,只要你不关心弹窗具体位置,直接用popover就能满足大多数需求。真需要指定位置,写几行CSS也不亏。
五、最骚操作:用select + popover实现自定义选择器
最近<select>元素支持了showpicker()方法,但还没完全统一。我一般是把<select>藏起来,自己用popover实现一个“下拉框”。
<button popovertarget="mymenu">
选择分类
</button>
<div id="mymenu" popover>
<h3>选择分类</h3>
<label><input type="radio" name="cat" value="tech"> 技术</label>
<label><input type="radio" name="cat" value="life"> 生活</label>
<label><input type="radio" name="cat" value="game"> 游戏</label>
</div>
点按钮弹出几个选项,而且再次点击外部自动关闭。你不需要额外写代码来把选项的值填回按钮里,那是JS的事,但至少交互层面已经原生完成了。
六、项目实战:一个轻量级的Tag输入气泡
上周产品让我做一个给文章添加标签的入口。点击“添加标签”按钮,弹出一个popover,里面是一个文本输入框和几个推荐标签,点推荐标签就自动添加到下方,点保存就把标签追加到页面上。
但我发现纯HTML做不了“点击标签后把值存入列表”这种状态操作。所以这部分还是需要一点点JS。但交互层,弹窗打开、关闭、点击外面消失仍然是纯HTML完成的。这里我只演示popover里的内容布局:
<button popovertarget="addTag">+ 添加标签</button>
<div id="addTag" popover>
<div>
<input type="text" placeholder="输入新标签">
<input type="button" value="添加">
</div>
<div>
<span class="tag">Java</span>
<span class="tag">CSS</span>
<span class="tag">HTML</span>
</div>
<form method="dialog" popover-target="addTag">
<button>完成</button>
</form>
</div>
你会发现,只要专心处理数据逻辑,UI交互层完全不需要管。代码量少了很多。
七、浏览器的兼容性和备用方案
目前所有主流浏览器(Chrome、Edge、Firefox、Safari)都已经支持dialog和popover的基本特性。话虽这么说,实际项目中还可能会遇到用户用旧版浏览器的情况,不过这种概率很小了。如果你实在要兼容旧Safari,可以写一个简单的特性检测:
if (typeof HTMLDialogElement !== 'undefined') {
// 支持 dialog
} else {
// 老办法,用display:block模拟
}
但popover属性就没办法了,就当是渐进增强:不支持的话,点击按钮没有任何反应,内容仍然藏在div里。你可以把popover的内容默认隐藏,然后做备选。但说真的,2025年了,还在用IE的用户基本忽略不计。
八、结合dialog和popover注意事项
- popover元素不需要额外加
display: none,浏览器默认隐藏空popover。 - 如果popover内嵌了表单,一定要用
method="dialog"否则点按钮可能把form提交到服务器。 - popover的层级默认在最顶层,比普通内容高,但不一定能压过普通dialog;嵌套dialog/popover时要留意层级。
- 一个popover可以同时被多个按钮触发:把相同的
id给各个按钮的popovertarget属性就行。
九、总结一下我的真实体验
以前看到“原生HTML组件”总觉得是笑话,但这一次dialog和popover确实拿得出手了。至少现在一些老项目里,很多弹窗和气泡交互完全可以去掉JS。对于追求简洁和性能的人来说,这绝对是一个惊喜。你甚至可以把这些特性用在Server-rendered页面里,直接让后端模板输出弹窗,不用绑定任何前端框架。
总之,如果最近你要做弹窗、下拉、气泡、提示,建议先试试原生<dialog>和popover,很可能写完之后把原来的JS弹窗库都删了。而且这俩特性还在发展中,以后只会越来越强。赶紧用起来吧。

