Chrome子窗口未关闭时window.open引用closed为true兼容问题
差异根因
这个返回值不一致是Chromium内核(Chrome、Edge等)和Firefox的同源安全策略实现差异导致的:
- Firefox会保留父窗口和跨域导航后子窗口的引用关联,
child.closed可以正确返回子窗口真实的开闭状态 - Chromium内核对跨域窗口做了更强的隔离:当子窗口导航到和父窗口不同源的地址(比如示例中打开谷歌后提交搜索跳转到结果页),父窗口持有的原始子窗口
window引用会被隔离,此时访问child.closed会直接返回true,和子窗口实际是否关闭没有关系,属于浏览器安全限制带来的误判。
跨浏览器兼容实现方案
不要直接依赖原生child.closed属性作为唯一判断依据,根据子窗口是否可控选择对应实现:
- 如果子窗口是同域可控的业务页面:通过
postMessage做双向通信,子窗口在beforeunload触发(关闭页面)时主动给父窗口发送关闭通知,父窗口收到通知后再标记子窗口为已关闭,这个方案判断精度100%。 - 如果子窗口是不可控的第三方跨域页面(比如示例中的外部站点):通过跨域访问异常捕获做兜底判断——访问跨域窗口的属性时如果抛出同源权限错误,说明子窗口仍然存在,只是处于跨域状态,不是真的关闭;只有访问属性不抛错且
child.closed为true时,才判定为子窗口真实关闭。
兼容代码示例
<html lang="zh-CN"> <head> <title>兼容判断示例</title> <script> function openChild() { let childRealClosed = false; const childWindow = window.open('http://google.com', 'auditor_child_tab', 'location=yes,status=yes,width=1366,height=768'); // 监听子窗口卸载事件,区分跨域跳转和真实关闭 childWindow.addEventListener('beforeunload', () => { setTimeout(() => { try { childRealClosed = childWindow.closed; } catch (crossOriginErr) { // 触发跨域权限错误说明子窗口仍在,只是跳转了跨域地址 childRealClosed = false; } }, 300); }); const statusCheckTimer = setInterval(() => { try { childRealClosed = childWindow.closed; } catch (crossOriginErr) { childRealClosed = false; } if (childRealClosed) { clearInterval(statusCheckTimer); console.log("子窗口已关闭"); } else { console.log("子窗口处于打开状态"); } }, 1000); } </script> </head> <body> <h1>测试页面</h1> <button onclick="openChild()">打开子窗口</button> </body> </html>
说明:受浏览器同源策略限制,针对完全不可控的第三方跨域子窗口,没有方案能做到100%无误差判断,上述实现可以解决Chrome下跨域跳转后
closed误返回true的问题,在Firefox和Chrome下的判断结果保持一致。
内容的提问来源于stack exchange,提问作者Abdul Gafoor
相关产品推荐
相关产品推荐

