javascript - 关闭窗口打破事件循环假设

标签 javascript internet-explorer internet-explorer-9

我遇到过一个小烦恼,但它会发展成一个大问题。

问题 1: 在 Internet Explorer 中,当您关闭窗口(通过 window.open 打开)时,ownerDocument 将随之消失

这意味着对 DOM 的任何调用,例如 appendChildcreateElement,都将失败并显示 SCRIPT70: Permission DeniedSCRIPT1717:接口(interface)未知

我查看了其他浏览器(例如 Chrome)的行为。在 Chrome 中,ownerDocument 仍然引用 #documentownerDocument.defaultView 最终将是 undefined。这对我来说很有意义。对 appendChildcreateElement 的调用将通过。只要您不尝试直接引用 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/

相关文章:

javascript - 过滤 Html 表的最有效方法?

javascript - 如何检测IE11并将其重定向到其他网站

css - 如何在 IE8 中使用 CSS 旋转和平移?

javascript - Vue CLI 3 防止某些输出文件的缓存破坏

javascript - 即使页面加载,如何保留或记住设置的日期

javascript - 如何将日期转换为给定格式?

javascript - 在 Internet Explorer 中添加对隐藏元素的选择会引发错误

internet-explorer-9 - IE 通过函数调用运行得更快?

jquery - 为什么我的 jQuery 插件不能在 IE9 中创建列?

css - IE9/8/7 css sprite 位置滑动问题