你以为 ::backdrop 只能改颜色?今天 Chrome 152 把事件系统也接进去了
配了三年弹层,每次需要「点击遮罩关闭弹层」这个交互,你都要写一坨复杂的点击坐标判断——今天 Chrome 152 把这件事彻底变了:::backdrop 终于可以绑定事件监听了。
之前 ::backdrop 只能做视觉层的事。设计师拿它改弹层背景色、媒体全屏时的边框颜色,但你想监听用户在 backdrop 上点了哪里,对不起,CSS 伪元素不归事件系统管。
所以「点遮罩关闭弹层」这套交互,长期靠的是 JS 坐标计算:拿到弹层的位置信息,算出在视口中的边界坐标,然后判断点击事件的 clientX/clientY 落在哪个区间。这个过程要处理滚动偏移、处理多层弹层嵌套,还要处理 dialog.showModal() 和 dialog.show() 行为不一致的问题。每个写过这个逻辑的工程师都知道这里有多少坑。
Chrome 152 把这个问题的根子变了。CSS Pseudo-Elements Level 4 早就定义了 CSSPseudoElement 这个 JavaScript 接口,让开发者可以用 JS 和 CSS 伪元素交互,但之前只支持 ::after、::before 和 ::marker 三个。Chrome 152 把支持列表扩展到了 ::backdrop、::scroll-marker 和 ::view-transition。
关键改变就在 ::backdrop 上:现在你可以在它上面绑事件监听了。
// 以前:复杂坐标判断
dialog.addEventListener(click, (e) => {
const rect = dialog.getBoundingClientRect();
if (
e.clientX < rect.left ||
e.clientX > rect.right ||
e.clientY < rect.top ||
e.clientY > rect.bottom
) {
dialog.close();
}
});
// 现在:直接监听 ::backdrop
const backdrop = dialog.matches(::backdrop)
? CSS.pseudo(dialog, backdrop)
: null;
if (backdrop) {
backdrop.addEventListener(click, () => dialog.close());
}
这就是 3 行 vs 10 行的差距,而且新写法不需要理解弹层在视口中的具体位置。
Chrome 152 的官方博客把这个能力形容为「不需要复杂的交叉计算」,意思是:你不再需要判断点击坐标落在弹层内还是外,::backdrop 本身就是弹层之外的那层遮罩,点它就是点遮罩,逻辑天然成立。
渐进增强写法推荐这样处理:
if (pseudo in CSS) {
const backdrop = CSS.pseudo(dialog, backdrop);
backdrop.addEventListener(click, () => dialog.close());
} else {
// 降级到坐标计算
}
还有一个值得关注的能力在 ::view-transition 那边:通过 CSSPseudoElement 拿到 ::view-transition 的引用之后,可以拦截正在执行中的视图过渡动画,根据当前动画元素的坐标实时决定下一个动画的走向。这个能力对复杂的状态流转动画特别有用,两个状态之间切换时不再需要预先写死所有过渡路径,可以根据当前位置动态判断。
目前 CSSPseudoElement 对 ::backdrop 的事件支持仅限 Chrome 152+ 和 Edge 152+。作为渐进增强特性,在生产环境中推荐先 check 再用,不支持的浏览器会走到原来的坐标计算分支,用户体验不受影响。
下一步:如果你的项目里有弹层组件是自研的,可以把这次升级当成一个重构窗口——把现有的坐标计算方案替换成 CSSPseudoElement 方案,代码行数能省一截,而且逻辑更干净。
评论区
登录后可评论。