JavaScript中如何捕获弹出窗口的关闭事件?

100

在弹出窗口关闭之前(使用window.open),我需要执行一些操作。

类似下面这样的代码会很好:

var new_window = window.open('some url')
new_window.onBeforeUnload = function(){ my code}

我该如何实现这个目标?

9个回答

156

虽然已经有一个正确的答案适用于同源环境,但我找到了一个可以解决跨域弹窗的方法:

var win = window.open('http://www.google.com'); 
var timer = setInterval(function() { 
    if(win.closed) {
        clearInterval(timer);
        alert('closed');
    }
}, 1000);

来源:atashbahar.com

对于那些考虑使用它的人来说。

即使Facebook在其Javascript SDK中也使用这个“技巧”。

您可以通过查看他们的代码来验证这一点。只需在https://connect.facebook.net/en_US/sdk.js中搜索.closed即可。


5
好的。谢谢! - Roshana Pitigala
7
我认为这并不是专门用于跨域的黑客行为,它实际上是检测窗口是否关闭的唯一准确方法。当窗口内的文档被卸载时,会触发unload和beforeunload事件,这可能是由于重定向或重新加载而导致的,因此并不一定意味着窗口已关闭。 - hemp
2
我刚刚使用Edge(2021年3月)测试了解决方案,其中我监视了同一域的窗口关闭事件,即在我的应用程序中打开然后关闭的其他窗口,这很好地运行! - OJB1
1
请注意!如果弹出窗口被阻止,这个setInterval函数将会无限运行。 - Arun Joseph
1
这种方法对我有效,但是即使打开窗口(win)内的URL发生更改,win.close也会被设置为true。有没有办法避免这种情况,只有在win实际关闭时才执行?@DustinHoffner - sumukha hegde
显示剩余3条评论

103

只要弹出窗口URL与父页面在相同的域名下,并将事件改为全部小写,您的示例就能正常运行

var new_window = window.open('some url')
new_window.onbeforeunload = function(){ /* my code */ }

8
如果新打开的窗口和父窗口不在同一个域下,是否有什么方法可以解决? - xiaohan2012
13
您最好的做法是在您的领域内打开一个文档,然后通过 iframe 加载远程 URL 或使用服务器脚本读取它,并从那里呈现它。 - glortho
1
@glortho 这种方法与在 new_window 中定义 onbeforeunload 事件相比有什么优势吗?更具体地说,如果这样做会立即将代码绑定到 new_window,甚至在 new_page 和它的脚本加载之前吗? - Khadim Ali
1
@xiaohan2012,这里有一个技巧。请查看我的回答:https://dev59.com/Bmox5IYBdhLWcg3wKBR9#48240128 - Dustin Hoffner
4
这并不是一个准确的检测窗口已关闭的方法。当窗口内的文档被卸载时,将触发unload和beforeunload事件,这可能是由于重定向或重新加载等原因引起的。请参阅@DustinHoffner的答案(https://dev59.com/Bmox5IYBdhLWcg3wKBR9#48240128)获取准确的方法。 - hemp
它也无法与重定向一起使用,所以假设您有一个OAuth流程,即使您的重定向URI具有相同的域,它也不会工作。 - Raj Shah

13
事件名称为onbeforeunload而非onBeforeUnload。JS区分大小写。

2
如果我们使用“on”来附加事件,它会将其作为内联属性附加到HTML上,而HTML是大小写敏感的,这意味着这样做不会有任何区别。 - nikoss

10

在打开窗口后,您需要将onbeforeunload事件绑定到窗口。

最简单的方法是将javascript onbeforeunload代码包含在窗口源代码中。

有关详细说明,请参阅Stackoverflow上这里这里两个非常相似的问题。


1
谢谢。确实,onbeforeunloadonunload必须在子窗口加载后绑定才能正常工作。 - Xavi

5

你可能不想覆盖 onbeforeunload 的值,因为这会干扰弹出窗口的处理程序,如果它也使用了那个钩子。

改用 addEventListener('beforeunload', ...)

const wnd = window.open(theUrl)
wnd.addEventListener('beforeunload', () => {
  // do stuff
})

请注意,有其他选项可用于事件,例如unloadpageHide。请阅读文档以确定哪个事件最适合您的实现。
参考信息如下: https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event https://developer.mozilla.org/en-US/docs/Web/API/Window/unload_event https://developer.mozilla.org/en-US/docs/Web/API/Window/pagehide_event 有关页面生命周期概述,请参见: https://developers.google.com/web/updates/2018/07/page-lifecycle-api#developer-recommendations-for-each-state

5

onbeforeunload事件应该在弹出窗口加载后添加。例如,您可以在其加载事件的末尾添加它,如下所示:

const new_window = window.open('some url')
new_window.onload = function(){ 
    /* my code */ 
    this.onbeforeunload = function(){ /* my code */ }
}

4
import { useEffect, useRef } from "react";

const usePopupWindow = ({
  url,
  onPopupClose,
  popupProperties = "width=200,height=200",
}) => {
  const timer = useRef();

  const handleWindowPopup = () => {
    const windowPopup = window.open(url, "popup", popupProperties);

    timer.current = setInterval(() => {
      if (windowPopup.closed) {
        clearInterval(timer.current);
        onPopupClose();
      }
    }, 1000);
  };

  useEffect(() => {
    return () => clearInterval(timer.current);
  }, []);

  return { handleWindowPopup };
};

export default usePopupWindow;

我为React使用场景创建了这个钩子


1

-1
var new_window = window.open('some_url')

你也可以检查new_window.window是否为null。但是你需要使用setTimeoutsetInterval函数手动检查。

顺便说一下,当你不在同一个域时,这是唯一的检查方式。


如果您有一个全局方法来为您打开和管理窗口,并且URL可能根据上下文是内部还是外部,则此功能非常有用。 - jimconte

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接