我遇到过一个小烦恼,但它会发展成一个大问题。
问题 1: 在 Internet Explorer 中,当您关闭窗口(通过 window.open
打开)时,ownerDocument
将随之消失
这意味着对 DOM 的任何调用,例如 appendChild
或 createElement
,都将失败并显示 SCRIPT70: Permission Denied
或 SCRIPT1717:接口(interface)未知
。
我查看了其他浏览器(例如 Chrome)的行为。在 Chrome 中,ownerDocument
仍然引用 #document
但 ownerDocument.defaultView
最终将是 undefined
。这对我来说很有意义。对 appendChild
和 createElement
的调用将通过。只要您不尝试直接引用 defaultView
,我认为一切都很好。
问题 2: 在 Internet Explorer 中,当您单击生成窗口的关闭按钮时,它似乎不遵守事件循环。我将 unload
事件附加到生成的窗口,它立即触发,而不是在事件循环结束时将其排队。这对我来说不有意义。处理这个相当微不足道的问题变得非常不可能。
如果我们只有问题 1,那么会有一个 - 仍然很痛苦但 - 直接的解决方案:检查 ownerDocument
是否存在,如果不存在则跳过。事实上,ownerDocument
在同步 JavaScript 代码中消失了。
预期行为:如果您引用了 DOM 节点,它不应消失 - 垃圾收集完整性。
预期行为 2:DOM 节点不应在同步代码中消失。 (当然除非你删除它)。
已知解决方法:将所有与 DOM 交互的代码移到窗口中,这样当窗口关闭时,JavaScript 运行时环境也会关闭。这不是一个简单的解决方案,可能需要对您的架构进行重大更改。
蹩脚的解决方案:将任何与 DOM 交互的函数包装在一个函数中,如果它检测到元素的窗口已关闭,该函数将消耗错误。这是非常具有侵入性的,并且对性能有重大影响,而 IE 已经很慢了。
有没有更好的解决方案?
至少,我想要的是一种忽略因用户关闭窗口而引发的任何错误
的方法。 问题 1 和问题 2 打破了您对 JavaScript 代码所做的基本假设:垃圾回收和事件循环。
演示脚本
<script type="text/javascript">
function go() {
var popup = window.open('', 'open', 'width=500,height=300,scrollbars=yes,resizable=yes');
popup.document.open();
popup.document.write('<html><head></head><body></body></html>');
popup.document.close();
for (var i = 0; i < 10000; i += 1) {
var node = popup.document.createTextNode(i + " ");
popup.document.body.appendChild(node);
}
}
</script>
<input type="button" onclick="go();" value="Open popup" />
(另存为 .html 文件)
说明:
- 在 Internet Explorer 9 中打开
- 点击“打开弹出窗口”
- 在渲染时关闭窗口
- 注意“权限被拒绝”
这是一个 JSFiddle:http://jsfiddle.net/C9p2R/1/
最佳答案
除非有人有更好的解决方案,否则我将采用蹩脚的解决方案。这是我的代码:
function apply_window_close_fix(dom_element, wrapped_element) {
var ignore_errors = false;
dom_element.ownerDocument.defaultView.addEventListener("unload", function () {
ignore_errors = true;
});
return map(wrapped_element, function (key, func) {
return function () {
try {
return func.apply(this, arguments);
} catch (e) {
if (ignore_errors === false) {
throw e;
}
}
};
});
}
wrapped_element
是我为修改 DOM 而返回的 API。我已经将所有函数包装在一个 try-catch 中,如果它发现窗口已关闭,它将忽略错误。我只为行为类似于 Internet Explorer 的浏览器调用此函数。
似乎只有非常小的性能影响。当然,这取决于您调用此 API 的密集程度。
一个小的缺点是目前重新抛出一些错误在一些浏览器中被破坏了。重新抛出 DOMException 会重置 Internet Explorer 和 Chrome(可能还有其他)中的堆栈。我还发现无法从 Internet Explorer 中的 DOMException 获取文件名和行号。又一次,粗略的疏忽最终只会浪费每个人的时间。
关于javascript - 关闭窗口打破事件循环假设,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14895844/