对于我的Web应用程序,我需要在父窗口关闭时关闭子窗口。 “关闭”是指浏览器窗口实际上已关闭,而不仅仅是导航到新页面。
我已经看过“How can I close the child window if the parent window is closed?”问题了,但我的问题已经延伸了。该问题的答案解决了在父级的任何卸载事件上关闭子窗口的问题。然而卸载!=关闭(IMO);只需点击一个链接即可触发卸载事件。
由于JS中没有“onclose”事件,我认为最好的方法是在子节点上父节点的unload事件setTimeout上查看它的父节点是否仍然存在,如果不存在则关闭:
var w = window.open("", "Logger", "height=480,width=640,resizeable,scrollbars=yes");
if (w) {
JSEvents.on(window,'unload',function(){
if (w && !w.closed) {
w.setTimeout(function(){
//IE this==w.opener
if (!w.opener || w.opener.closed) {
w.close();
}
},500);
}
});
}
但是,我相信我已经非常确切地表明,在IE(7)中,您无法在父窗口或子窗口上的卸载事件期间使用setTimeout。在上面的例子this == w.opener
里面的setTimeout匿名函数。此测试从不产生警报:
JSEvents.on(window, 'unload', function(){
window.setTimeout(function(){alert('HERE');},500);
});
没有alert
的直线setTimeout
会产生警报。
是否有从我可以使用的父级设置子项的setTimeout的技巧?
是否有其他方法可以检测我父母何时关闭?
在FF中更容易做到,所以我专注于让它在IE下工作。
答案 0 :(得分:5)
是否有从我可以使用的父级设置子项的setTimeout的技巧?
您无法使用IE中父级的代码执行此操作。当IE关闭一个窗口时,你从其中的代码定义的成员就会消失,对这些成员的引用(例如指向你的函数的子超时)将被悬空。根据你所拥有的IE版本,可能什么都不会发生,或者你可能会得到“无法从释放的脚本执行代码”错误。
你可以在孩子里面做。父可以在子onunload上设置一个标志(例如,w.parentUnloaded = true),该子标志上的setInterval轮询器可以检查并自行关闭 -
if (window.parentUnloaded && (!window.opener || window.opener.closed))
这是IE漏洞吗?那么......当然,其他浏览器对卸载脚本的反应不同。但没有标准说明应该在这里发生什么。即使在同一浏览器系列中,行为也会随着浏览器的更新而改变,以避免跨上下文脚本问题。
使用这样的东西和事件计时问题(*),跨窗口脚本比看起来要困难得多。通常最好避免;如果您可以将“弹出窗口”放在主页面的div中,通常最好这样做。
(*:有些情况(**)可以在一个窗口中触发事件并执行,而另一个窗口中的JavaScript仍处于运行过程中。因此窗口'a'可以调用窗口上的方法'b '并且在窗口'b'中的其他代码仍然在进行时执行。如果它们是在正常的JavaScript假设下一次只激活一个执行线程,那么这可能会极大地混淆窗口'b'中的脚本。这就是为什么我建议在子节点中使用轮询器而不是让父节点明确地调用子节点。将来我们将使用HTML5的postMessage方法来避免这些问题。)
(**:你可以说这绝不应该发生,而且肯定很奇怪,但它确实发生在许多浏览器中,特别是涉及模态对话或某些版本的IE Sun Java插件时使用。)
关闭应用程序后,您不能只留下弹出式窗口;这只是不礼貌。
有人会说,不礼貌是首先打开弹出窗口。 ; - )
在我看来,无论用户是在关闭窗口,还是只是导航回他的主页,书签,或者输入一个子窗口,关闭任何子窗口都是有意义的。地址等等。就个人而言,如果我试图将应用程序“重置”为开始状态,我可能也想要在刷新时丢失子窗口。
如果你有多个文件,父母将在同一个应用程序的所有部分之间导航,不应该关闭孩子,你自己的事情真的很难! :-)但是你可以调整上面的“子窗口,如果'的方法试图嗅探opener.location并查看是否在你的应用程序中决定是否关闭。诀窍是如果开启者被导航到另一个域,访问会抛出一个安全异常,所以你必须在try ... catch块中包含位置访问,如果开启者位置也关闭了窗口是不可读的。
bucabay写道(和安东尼类似):
刷新或关闭窗口后,浏览器会认为窗口已关闭。因此,就孩子而言,一旦刷新父母,它的开启者就会消失。
这是非常明智和合乎逻辑的。浏览器可能 每个文档都有一个“窗口”。但试试吧 - 他们没有。子弹出窗口保留对其开启者的访问权限(并且,只要该开启者是相同安全上下文中的文档,开启者的内容),通过刷新开启者,在IE / FF / Op / Saf /中字符。
答案 1 :(得分:2)
在弹出窗口中:(如果开启者已关闭或更改域,则此方法有效)
var int = window.setInterval(function(){
// On opener domain change, all browsers throw an error. Lets use that error to our advantage using try/catch:
try
{
if(opener && typeof opener.document != 'undefined')
{
// Adding this variable fixes IE8. Why? Because F U thats why.
var openerRef = window.opener.location.host;
}
else{
// Loads the survey when opener is closed
window.location = 'exit-survey.jsp';
}
}
// Loads the survey if an error throws (error throws when opener changes domain)
catch(err)
{
window.location = 'exit-survey.jsp';
}
}, 500);
})
答案 2 :(得分:1)
您是否尝试在打开的窗口中创建函数,如下所示:
window.closeWithParent = function() {
if (!window.opener || window.opener.closed) {
window.close();
}
};
window.parentClosing = function() {
window.setTimeout(window.closeWithParent, 500);
};
然后从您的父窗口:
JSEvents.on(window,'unload', function() {
if (w.parentClosing) w.parentClosing();
});
我不确定,但我认为跨窗口与窗口对象连接可能会导致您遇到的问题。此外,这样在子窗口范围(希望)中调用setTimeout而不是正在卸载的父窗口(这会丢失任何超时)。
答案 3 :(得分:1)
问题是从浏览器的角度来看,窗口是为了显示文档内容而创建的概念。当您从该文档导航到另一个窗口时,该窗口将关闭并创建一个新窗口。
浏览器概念窗口由浏览器拥有的实际客户端窗口托管,并且客户端窗口可能被重新用于显示后续文档这一事实实际上并不是您的任何业务(如果您不介意短语)。
浏览器选择显示内容的方式,具有可能被用户点击的关闭按钮的窗口超出了大多数浏览器认为任何主机文档需要知道的内容。
因此,如果可行的话,你现在可能发明的任何伎俩都可以通过更严格的安全性在浏览器的更高版本中关闭。
我的建议是放弃这个要求。
答案 4 :(得分:1)
刷新或关闭窗口后,浏览器会认为窗口已关闭。因此,就孩子而言,一旦刷新父母,它的开启者就消失了。
因此,您无法测试是否刷新了窗口,或使用JavaScript引用在同一域上打开了另一个实例。 (例如window.opener)
但是,您可以创建对其他窗口的间接引用,并将它们保存在任何跨窗口的浏览器存储中,甚至是服务器存储中。使存储反映窗口的状态将允许其他窗口观察该窗口,即使它们没有引用。
你可以使用cookies,或DOM Storage等。我有一个使用cookies的库(它是一年前写的,当时不支持DOM存储 - FF2 +,IE8 +我认为)。如果你想看到它作为一个例子,我可以做到这一点。
无论如何,你可以做的是保留一段代表父窗口的数据。定期更新,并从孩子那里进行轮询。
使用Cookie的示例:
// parent
setInterval(function() { setCookie('parent_alive', new Date()) }, 1000);
// child
setInterval(function() { if (readCookie('parent_alive') < new Date()-5000) window.close() }, 1000)
在父母没有更新cookie“parent_alive”之后,孩子将关闭5秒。主要问题是互联网连接可能会阻止页面加载5秒钟,孩子认为该页面已关闭。所以这是一种平衡行为。
请注意,如果您使用会话cookie,则轮询非常有效,因为它们会留在内存中。但是,如果你使用持久性cookie,你可能会遇到会很糟糕的磁盘。
答案 5 :(得分:1)
将脚本添加到子窗口范围的正确方法似乎是使用DOM来创建脚本标记。以下代码用于检查父窗口在IE中卸载后是否仍然打开四分之一秒。
var w = window.open("", "Logger", "height=480,width=640,resizeable,scrollbars=yes");
if (w) {
JSEvents.on(window,'unload',function(){
if (w && !w.closed) {
var srpt = w.document.createElement('script');
srpt.type = 'text/javascript';
srpt.text = 'window.setTimeout(function(){if(!window.opener||window.opener.closed){window.close();}},250);';
w.document.body.appendChild(srpt);
}
});
}
感谢大家帮助我指明这个方向。解决方案只是弄清楚如何动态插入带有文本内容而不是src
的新脚本标记。