滑动和右键明明不是点击,popover 却一直当成点了外面——Chrome 153 让 light dismiss 只认 click,弹窗不再自己关掉

移动端上打开一个筛选面板,手指放上去想往下多看几个选项——面板直接没了。桌面端更憋屈:popover 里一段文字想右键复制,菜单还没出来,popover 先关了。

这两件事的根子是同一个:浏览器判断「要不要关闭弹窗」的方式,从一开始就把不是点击的动作算成了点击。

light dismiss 是什么,它是怎么误判的

给元素加上 popover 属性(或给 <dialog> 开启点击外部关闭)之后,点弹窗外面一下就会把它关掉,这个行为有个正式名字:light dismiss(轻量关闭)。

Chrome 153 之前的判定机制是这样的:浏览器监听 pointerdown + pointerup 这一对事件,只要「按下」和「抬起」都发生在弹窗外面,就认定用户点了外面,关。

问题在于,这两个事件根本不是「点击」的专属:

  • 触摸滚动:手指落在屏幕上必然先产生 pointerdown,滑动结束后产生 pointerup。在弹窗内滚动内容,这一对事件序列常常被判定为「在弹窗外按下又抬起」——滚动还没看两行,弹窗关了。
  • 右键:鼠标右键同样产生 pointerdown / pointerup,紧接着才是 contextmenu。想复制弹窗里的文字,右键一下弹窗就没了。

Chromium 为此立了两个 bug:408010435(tracking)和 425579196。

修复方式:只认 click

2026-08-11,Chromium 工程师 Joey Arhar 在 blink-dev 发出 Web-Facing Change PSA:light dismiss 的触发条件从「pointerdown + pointerup 组合」改为只响应 click 事件。

这个改法之所以干净,是因为 click 在规范层面天然排除了这两类误触发:滚动不会产生 click(按下后有位移,浏览器不派发 click);右键产生的是 contextmenu,不是 click。规范改动同步在 WHATWG HTML PR #11536(”Change light dismiss to use click events”,2025-08-06 提交)和 w3c/pointerevents PR #460。

Before(Chrome 153 之前):

<!-- 需要滚动的筛选面板 -->
<button popovertarget="filters">筛选</button>
<div id="filters" popover>
  <ul style="max-height: 40vh; overflow: auto">
    <li>选项 A</li> <!-- …… 20 个选项,超出高度需要滚动 -->
  </ul>
</div>

手指在列表上滑动 → 浏览器收集到弹窗外的 pointerdown/pointerup → 判定为点击外部 → 面板关闭。桌面端右键复制文字同理,contextmenu 还没弹,popover 已经没了。

After(Chrome 153+):

同样的代码,一行都不用改。滑动就是滑动,面板保持打开;右键只弹系统菜单,面板保持打开;只有真正按下后原位抬起、无位移的 click 落在弹窗外面,才会关闭。

版本时间线与浏览器覆盖

浏览器 状态 说明
Chrome 153(桌面 / Android / WebView) ✅ 已上线 2026-08-11 PSA,随 153 发车
Edge 154 ✅ 已上线 2026-09-24 Edge 154 web platform release notes 收录
Firefox ⬜ 无信号 chromestatus 记录 Gecko: No signal
Safari ⬜ 无信号 chromestatus 记录 WebKit: No signal

标准测试已就位:WPT 用例 popover-light-dismiss-touch-scroll.html 和 popover-light-dismiss-contextmenu.html 分别盯滚动和右键两条路径——这也说明该行为是规范级变更,不是某一家的私货。

对你现有代码的影响

  1. 原生 popover/dialog 用法:零改动。 这是行为修复,没有新 API,没有 feature detection 可做(也没有必要做)——用户升级到 Chrome 153+,误关现象自动消失。
  2. 自己手写 light dismiss 的组件:值得回头清理。 如果你之前为了绕开误触发,自己在 document 上监听 pointerdown 判断坐标再关弹窗,那套逻辑现在既多余、又会在旧代码里复现同样的滚动误关。原生 popover 属性就是干这个的(不想外部关闭就用 popover="manual"),是时候把手工判定删了。
  3. 跨浏览器仍要留一手。 Firefox / Safari 尚未表态跟进,Safari 上「滚动不关闭」依旧不成立。涉及长内容弹窗的移动端页面,别把「用户能滚动完」当作所有浏览器都成立的前提。

下一步

打开你项目里最长的那个 popover / 下拉面板,在 Chrome 153+ 的手机模拟器上做一次滑动测试:内容能不能滚到底、面板还在不在。能滚到底,这个坑你已经跨过去了;滚一半就关,先确认你的组件是不是还在用 pointerdown 手工判定——那条路在任何浏览器里都会误关。

评论区

0 条评论

登录后可评论。

阿跨·跨端开发 14 阅读