Open multiple pop up windows in safari 11

Viewed 2166

Since the safari 11 update on September 20 - the following code (Javascript) only opens 1 window at a time (on safari 10.1 it opens them all).

Is it possible to do this in safari 11 and if yes, how?

My code (just an example):

window.open("https://www.stackoverflow.com");
window.open("https://www.google.com");
window.open("https://www.youtube.com");

Update :

  1. The browser preferences are set to enable pop ups and to not block anything.
  2. The code works if I set a "setTimeout()" between each window open of at least 0.5 seconds - this may be because maybe the new update of safari does not want to let me "spam" the user with too many pop up windows.
1 Answers

Update: This question and solution is still valid as of Safari 13.

First, here's the behaviour of Safari 11 I've observed from testing:

  • One popup is always allowed to open (even when the global "Block pop-ups" setting is checked). When "Block pop-up windows" is checked, access to the popup's window object is restricted (not the case when "Block pop-up windows" is unchecked).
  • As you've described, when "Block pop-up windows" is unchecked, multiple window.open calls must be delayed (>1s required in my testing).
  • When "Block pop-up windows" is checked, it seems only one popup will ever be allowed to open (despite delay used).

So, you've discovered one way to get around this: Add a delay.

Here's one more way that will allow you to open multiple popups without the need for a delay, using the knowledge that when "Block pop-up windows" is unchecked, each window may open one pop-up without a delay. With the three popups in your example in mind, here's the general flow:

  1. Open a popup (blank page) on your domain.
  2. Delegate the opening of the next popup to this popup by injecting a script that does it on load.
  3. Redirect the first popup to your desired URL.
  4. Repeat until all your pop-ups are open.

The following is what I've built to handle this flow:

/**
 * Handle the passed hrefs for Safari, which requires special/different
 * handling than other browsers. Open each one in a new window (popup)
 * and delegate the opening of the next popup to each new popup. Handle
 * Safari's global popup blocker setting and inform the primary page
 * (via postMessage) when the blocker is enabled, so a notification can
 * be shown to the user.
 *
 * @param  {Array}    hrefs             hrefs of popups to open
 * @param  {Function} safariPopupOpener Self reference. Required for
 *                                      injecting into next popup.
 * @param  {Window}   primaryWindow     Reference to the primary page
 *                                      Window object. Required for
 *                                      sending postMessage back.
 * @param  {string}   blockedMessage    Message body to send back in
 *                                      postMessage.
 */
var safariPopupOpener = function(
    hrefs,
    safariPopupOpener,
    primaryWindow,
    blockedMessage
) {
    var newWindow = window.open('//url/of/the/blank/page/on/your/domain');
    var popupOpenerScript = document.createElement('script');

        // Must add these all to the popup's window object as the
        // execution context of opener() below where they're used is the
        // next popup, not the current window
        newWindow.openAllResultHrefs = hrefs;
        newWindow.openAllResultOpener = safariPopupOpener;
        newWindow.primaryWindow = primaryWindow;
        newWindow.blockedMessage = blockedMessage;

        /**
         * Logic to inject into the popup
         */
        function opener() {
            var hrefsCopy = window.openAllResultHrefs.slice();

            // Delete the first item from the array for injecting into
            // the next popup
            hrefsCopy.shift();

            if (hrefsCopy.length > 0) {
                // Even when popups are blocked in Safari, one popup is
                // always allowed to open. However any other popups
                // opened sequentially are blocked. Also, access to the
                // one popup's window object is restricted, so this
                // tries to open the second popup, if window object is
                // restricted (which occurs before another popup is
                // opened), catches the resulting error, closes the
                // first popup and sends a message back to the primary
                // page that popups are blocked.
                try {
                    window.openAllResultOpener(
                        hrefsCopy,
                        window.openAllResultOpener,
                        window.primaryWindow,
                        window.blockedMessage
                    );
                } catch (e) {
                    // Optional: Send a message back to the primary page that
                    // popups have been blocked
                    window.primaryWindow.postMessage(
                        window.blockedMessage,
                        window.primaryWindow.origin
                    );

                    // Close the (first) popup window (first because
                    // we only reach this case when popups are blocked
                    // and we've only successfully opened one popup)
                    window.close();
                }
            }

            // Redirect to the popup href
            window.location.href = window.openAllResultHrefs[0];
        }

    // Inject the self-executing opener function so it'll run on load in
    // the opened popup
    popupOpenerScript.innerHTML = '(' + opener.toString() + '());';
    newWindow.addEventListener('load', function() {
        // Append the script to the new window's body
        this.document.body.appendChild(popupOpenerScript);
    });
}
  • Note that I'm also detecting blocking and sending a postMessage back to the primary window so it can handle the blocking (e.g. display a message to the user). So, would require a message listener on the primary page.
  • postMessage may not be necessary but I couldn't access window.opener when popups were blocked. Also probably lots of room for improvement, but I've already spent too much time on this :-))
Related