Javascript – Detect blocked popup in Chrome

google-chromejavascriptpopup

I am aware of javascript techniques to detect whether a popup is blocked in other browsers (as described in the answer to this question). Here's the basic test:

var newWin = window.open(url);

if(!newWin || newWin.closed || typeof newWin.closed=='undefined')
{
    //POPUP BLOCKED
}

But this does not work in Chrome. The "POPUP BLOCKED" section is never reached when the popup is blocked.

Of course, the test is working to an extent since Chrome doesn't actually block the popup, but opens it in a tiny minimized window at the lower right corner which lists "blocked" popups.

What I would like to do is be able to tell if the popup was blocked by Chrome's popup blocker. I try to avoid browser sniffing in favor of feature detection. Is there a way to do this without browser sniffing?

Edit: I have now tried making use of newWin.outerHeight, newWin.left, and other similar properties to accomplish this. Google Chrome returns all position and height values as 0 when the popup is blocked.

Unfortunately, it also returns the same values even if the popup is actually opened for an unknown amount of time. After some magical period (a couple of seconds in my testing), the location and size information is returned as the correct values. In other words, I'm still no closer to figuring this out. Any help would be appreciated.

Best Solution

Well the "magical time" you speak of is probably when the popup's DOM has been loaded. Or else it might be when everything (images, outboard CSS, etc.) has been loaded. You could test this easily by adding a very large graphic to the popup (clear your cache first!). If you were using a Javascript Framework like jQuery (or something similar), you could use the ready() event (or something similar) to wait for the DOM to load before checking the window offset. The danger in this is that Safari detection works in a conflicting way: the popup's DOM will never be ready() in Safari because it'll give you a valid handle for the window you're trying to open -- whether it actually opens or not. (in fact, i believe your popup test code above won't work for safari.)

I think the best thing you can do is wrap your test in a setTimeout() and give the popup 3-5 seconds to complete loading before running the test. It's not perfect, but it should work at least 95% of the time.

Here's the code I use for cross-browser detection, without the Chrome part.

function _hasPopupBlocker(poppedWindow) {
    var result = false;

    try {
        if (typeof poppedWindow == 'undefined') {
            // Safari with popup blocker... leaves the popup window handle undefined
            result = true;
        }
        else if (poppedWindow && poppedWindow.closed) {
            // This happens if the user opens and closes the client window...
            // Confusing because the handle is still available, but it's in a "closed" state.
            // We're not saying that the window is not being blocked, we're just saying
            // that the window has been closed before the test could be run.
            result = false;
        }
        else if (poppedWindow && poppedWindow.test) {
            // This is the actual test. The client window should be fine.
            result = false;
        }
        else {
            // Else we'll assume the window is not OK
            result = true;
        }

    } catch (err) {
        //if (console) {
        //    console.warn("Could not access popup window", err);
        //}
    }

    return result;
}

What I do is run this test from the parent and wrap it in a setTimeout(), giving the child window 3-5 seconds to load. In the child window, you need to add a test function:

function test() {}

The popup blocker detector tests to see whether the "test" function exists as a member of the child window.

ADDED JUNE 15 2015:

I think the modern way to handle this would be to use window.postMessage() to have the child notify the parent that the window has been loaded. The approach is similar (child tells parent it's loaded), but the means of communication has improved. I was able to do this cross-domain from the child:

$(window).load(function() {
  this.opener.postMessage({'loaded': true}, "*");
  this.close();
});

The parent listens for this message using:

$(window).on('message', function(event) {     
  alert(event.originalEvent.data.loaded)
}); 

Hope this helps.