Chrome v95及以上版本JS弹窗拦截检测代码失效问题求助
问题根因
这个异常是Chrome 95+版本的弹窗权限缓存机制导致的,原有检测逻辑存在覆盖盲区:
- 站点首次全新加载时,Chrome会实时读取当前站点的弹窗权限配置:如果权限为拦截,
window.open会直接返回null,或者返回一个立即标记为关闭的空窗口对象,原有的三重判断(!newWin/newWin.closed/closed属性不存在)可以正常命中拦截状态。 - 一旦用户曾给当前站点授予过弹窗权限,Chrome会在渲染进程中缓存「允许弹窗」的权限标记。即使用户后续在站点设置中把权限改回拦截,这个缓存不会实时同步给已经打开的页面上下文。此时调用
window.open不会返回null,而是返回一个待拦截状态的窗口引用:这个引用的closed属性初始值为false,closed属性本身也存在,原有的判断条件全部不满足,就会误判为弹窗允许。 - 这个场景下Chrome虽然返回了窗口引用,但会在后台静默拦截弹窗,不会真的加载目标页面,也不会让弹窗出现在用户视野里。
修复方案
原有的同步判断逻辑无法覆盖权限缓存场景,需要补充异步检测逻辑,结合窗口的生命周期状态综合判断,修复后的完整实现如下:
/** * 检测弹窗是否被拦截 * @param {string} url 要打开的目标地址 * @returns {Promise<boolean>} true-弹窗未被拦截 false-弹窗被拦截 */ function checkPopupBlocked(url) { return new Promise((resolve) => { const newWin = window.open(url); // 第一层:原有同步检测逻辑,覆盖首次加载直接拦截的场景 if (!newWin || newWin.closed || typeof newWin.closed === 'undefined') { resolve(false); return; } let detectCount = 0; // 第二层:异步轮询检测,覆盖权限缓存后延迟拦截的场景 const detectTimer = setInterval(() => { detectCount++; try { // 尝试读取窗口内部地址,触发同域/拦截状态校验 newWin.location.href; if (newWin.closed) { clearInterval(detectTimer); resolve(false); return; } // 连续检测25次(共2.5秒)窗口仍正常存在,判定为未拦截 if (detectCount > 25) { clearInterval(detectTimer); resolve(true); } } catch (e) { // 跨域正常弹窗访问location会抛跨域错误,属于正常现象,仅当窗口被自动关闭时判定为拦截 if (newWin.closed) { clearInterval(detectTimer); resolve(false); return; } if (detectCount > 25) { clearInterval(detectTimer); resolve(true); } } }, 100); // 兜底监听窗口提前关闭事件 newWin.addEventListener('beforeunload', () => { setTimeout(() => { if (newWin.closed && detectCount < 10) { clearInterval(detectTimer); resolve(false); } }, 0); }); }); } // 调用示例 checkPopupBlocked('你的目标跳转地址').then(isPopupAllowed => { alert(isPopupAllowed ? 'enabled' : 'blocked'); });
核心调整点:
- 把同步判断改成异步检测:Chrome对缓存权限下的弹窗拦截是异步执行的,返回引用后几百毫秒才会真正关闭窗口,同步读取属性拿不到准确状态
- 增加100ms间隔的短轮询,最多检测2.5秒:同时校验窗口的
closed状态、内部属性可访问性,区分正常打开的弹窗(含跨域弹窗)和被拦截的弹窗 - 区分跨域报错和拦截报错:正常打开的跨域弹窗访问内部
location会触发跨域安全错误,但窗口不会被自动关闭;被拦截的弹窗会在短时间内被Chrome自动关闭,结合这个特征可以准确识别拦截状态 - 增加定时器清理逻辑,避免内存泄漏,同时兼容用户手动快速关闭窗口的极端场景
注意:所有弹窗检测逻辑必须在用户主动交互事件(如click、touchstart)的同步调用栈中触发,Chrome对非用户交互触发的弹窗默认全局拦截,不存在可绕过的检测方案。
内容的提问来源于stack exchange,提问作者sbh
相关产品推荐
相关产品推荐

