24

I am opening a popup window and attaching an onbeforeunload event to it like this:

win = window.open("http://www.google.com", "", "width=300px,height=300px");
win.onbeforeunload = function() {
    //do your stuff here
    alert("Closed");
};

If I leave the URL empty, the new popup opens with "about:blank" as the address but when I close it, I see the alert.

If I open in as you see it (with an external URL), once it's closed, I cannot see the alert anymore. Any idea why this is happening?

Zorrocaesar
  • 748
  • 1
  • 7
  • 22

4 Answers4

73

As mentioned, same origin policy prevents Javascript from detecting such events. But there's a quite simple solution which allows you to detect closure of such windows.

Here's the JS code:

var openDialog = function(uri, name, options, closeCallback) {
    var win = window.open(uri, name, options);
    var interval = window.setInterval(function() {
        try {
            if (win == null || win.closed) {
                window.clearInterval(interval);
                closeCallback(win);
            }
        }
        catch (e) {
        }
    }, 1000);
    return win;
};

What it does: it creates new window with provided parameters and then sets the checker function with 1s interval. The function then checks if the window object is present and has its closed property set to false. If either ot these is not true, this means, that the window is (probably) closed and we should fire the 'closeCallback function' callback.

This function should work with all modern browsers. Some time ago Opera caused errors when checking properties from windows on other domains - thus the try..catch block. But I've tested it now and it seems it works quite ok.

We used this technique to create 'facebook-style' login popups for sites which doesn't support them via SDK (ehem... Twitter... ehem). This required a little bit of extra work - we couldn't get any message from Twitter itself, but the Oauth redireced us back to our domain, and then we were able to put some data in popup window object which were accessible from the opener. Then in the close callback function we parsed those data and presented the actual results.

One drawback of this method is that the callback is invoked AFTER the window has been closed. Well, this is the best I was able to achieve with cross domain policies in place.

Tomasz Struczyński
  • 3,273
  • 23
  • 28
  • Awesome! Thanks a lot for the solution, should be upvoted 100x. – Eugene Jul 24 '13 at 21:48
  • Well, I really do not understand «if» statement. If I output «win» with «console.log(win, typeof win)» inside setInterval function it always returns «Window {}» and «object». When the popup is closed it also outputs «Window {}» and «object» and then setInterval stops. So, it never is null and it has no any «closed» property... but everything works perfectly! How? P.S. It is in Google Chrome. – artuska Mar 12 '15 at 19:27
  • In Mozilla FF it outputs «Window → https://www.facebook.com/login.php» and «object». When the popup is closed it outputs Window object with «close» method. So, FF is looking for «closed:true» property and closes the popup. – artuska Mar 12 '15 at 19:35
  • Well, maybe Crome also has «closed» property but just does not output it correctly in console. – artuska Mar 12 '15 at 19:37
  • IE11 outputs «null» and «object» from the start :) – artuska Mar 12 '15 at 19:39
2

You could listen to the 'focus' event of the opener window which fires when the user closes the popup.

Andrew
  • 510
  • 5
  • 6
  • 1
    It's as simple as this: window.addEventListener("focus", function() { // this fires when the user closes the popup }); window.open(...) – Andrew Jun 19 '14 at 10:07
  • So - this sounded like a cleaner approach than an interval, but it didn't work :/. When the focus fires, window.closed still returns true – Colin Oct 01 '14 at 19:16
  • On Chrome this fired when the window was opened. – Aseem Bansal Sep 18 '15 at 06:51
1

Unfortunately, you're trying to communicate across domains which is prohibited by JavaScript's same origin policy. You'd have to use a server-side proxy or some other ugly hack to get around it.

You could try creating a page on your site that loads the external website in an iframe. You could then pop open that page and listen for it to unload.

chrx
  • 3,524
  • 1
  • 15
  • 17
  • 3
    That was my though too, but the funny thing which got me confused is that once I open the popup window, I am able to close it (win.close()) from it's parent or to focus it (win.focus()). Why do these work and onbeforeunload() doesn't? – Zorrocaesar Mar 29 '13 at 07:45
1

I combined @ThomasZ's answer with this one to set an interval limit (didn't want to use setTimeout).

Example (in Typescript, declared anonymously so as not lose reference to "this"):

  private _callMethodWithInterval = (url: string, callback: function, delay: number, repetitions: number) => {      
    const newWindow = window.open(url, "WIndowName", null, true);

    let x = 0;
    let intervalID = window.setInterval(() => {
      //stops interval if newWindow closed or doesn't exist
      try {
        if (newWindow == null || newWindow.closed) {
          console.info("window closed - interval cleared")
          callback();
          window.clearInterval(intervalID);
        }
      }
      catch (e) {
        console.error(`newWindow never closed or null - ${e}`)
      }
      //stops interval after number of intervals
      if (++x === repetitions) {
        console.info("max intervals reached - interval cleared")        
        window.clearInterval(intervalID);
      }
    }, delay)
  }//end _callMethodWithInterval  
barakbd
  • 988
  • 10
  • 16