Обнаружить заблокированное всплывающее окно в Chrome - PullRequest
100 голосов
/ 21 марта 2009

Мне известны методы javascript, позволяющие определить, заблокировано ли всплывающее окно в других браузерах (как описано в ответ на этот вопрос ). Вот основной тест:

var newWin = window.open(url);

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

Но это не работает в Chrome. Раздел «POPUP BLOCKED» никогда не появляется, когда всплывающее окно заблокировано.

Конечно, тест работает до некоторой степени, поскольку Chrome на самом деле не блокирует всплывающее окно, а открывает его в крошечном свернутом окне в правом нижнем углу, в котором перечислены «заблокированные» всплывающие окна.

Что я хотел бы сделать, так это определить, было ли всплывающее окно заблокировано блокировщиком всплывающих окон Chrome. Я пытаюсь избежать перехвата браузера в пользу обнаружения функций. Есть ли способ сделать это без перехвата браузера?

Редактировать : сейчас я попытался использовать newWin.outerHeight, newWin.left и другие подобные свойства для достижения этой цели. Google Chrome возвращает все значения положения и высоты как 0, когда всплывающее окно заблокировано.

К сожалению, он также возвращает те же значения, даже если всплывающее окно действительно открывается в течение неизвестного периода времени. После некоторого магического периода (несколько секунд в моем тестировании) информация о местоположении и размере возвращается как правильные значения. Другими словами, я все еще не приблизился к выяснению этого. Любая помощь будет оценена.

Ответы [ 16 ]

63 голосов
/ 07 июля 2009

Что ж, "волшебное время", о котором вы говорите, возможно, это когда DOM всплывающего окна загружено. Или же это может быть, когда все (изображения, внешний CSS и т. Д.) Были загружены. Вы можете легко проверить это, добавив во всплывающее окно очень большую графику (сначала очистите кэш!). Если вы использовали Javascript Framework, такой как jQuery (или что-то подобное), вы можете использовать событие ready () (или что-то подобное), чтобы дождаться загрузки DOM, прежде чем проверять смещение окна. Опасность в этом заключается в том, что обнаружение Safari работает конфликтующим образом: DOM всплывающего окна никогда не будет готово () в Safari, потому что оно даст вам действительный дескриптор для окна, которое вы пытаетесь открыть - независимо от того, открывается оно или нет. не. (На самом деле, я полагаю, что приведенный выше тестовый код не будет работать для сафари.)

Я думаю, что лучшее, что вы можете сделать, - это обернуть ваш тест в setTimeout () и дать всплывающему окну 3-5 секунд, чтобы завершить загрузку перед запуском теста. Это не идеально, но должно работать как минимум 95% времени.

Вот код, который я использую для кросс-браузерного обнаружения без Chrome.

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;
}

Что я делаю, так это запускаю этот тест от родителя и оборачиваю его в setTimeout (), давая дочернему окну 3-5 секунд для загрузки. В дочернем окне вам нужно добавить тестовую функцию:

функциональный тест () {}

Детектор блокировщика всплывающих окон проверяет, существует ли функция «test» как член дочернего окна.

ДОБАВЛЕНО 15 ИЮНЯ 2015 ГОДА:

Я думаю, что современный способ справиться с этим - использовать window.postMessage (), чтобы дочерний элемент уведомил родителя о том, что окно было загружено. Подход аналогичен (ребенок говорит родителю, что он загружен), но средства связи улучшились. Мне удалось сделать этот междоменный домен от ребенка:

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

Родитель прослушивает это сообщение, используя:

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

Надеюсь, это поможет.

15 голосов
/ 25 февраля 2011

Только одно улучшение во фрагменте InvisibleBacon (протестировано в IE9, Safari 5, Chrome 9 и FF 3.6):

var myPopup = window.open("popupcheck.htm", "", "directories=no,height=150,width=150,menubar=no,resizable=no,scrollbars=no,status=no,titlebar=no,top=0,location=no");
if (!myPopup)
    alert("failed for most browsers");
else {
    myPopup.onload = function() {
        setTimeout(function() {
            if (myPopup.screenX === 0) {
                alert("failed for chrome");
            } else {
                // close the test window if popups are allowed.
                myPopup.close();  
            }
        }, 0);
    };
}
12 голосов
/ 01 декабря 2012

Ниже приведено jQuery решение для проверки блокировки всплывающих окон. Он был протестирован в FF (v11), Safari (v6), Chrome (v23.0.127.95) и IE (v7 и v9). Обновите функцию _displayError, чтобы обрабатывать сообщение об ошибке по своему усмотрению.

var popupBlockerChecker = {
        check: function(popup_window){
            var _scope = this;
            if (popup_window) {
                if(/chrome/.test(navigator.userAgent.toLowerCase())){
                    setTimeout(function () {
                        _scope._is_popup_blocked(_scope, popup_window);
                     },200);
                }else{
                    popup_window.onload = function () {
                        _scope._is_popup_blocked(_scope, popup_window);
                    };
                }
            }else{
                _scope._displayError();
            }
        },
        _is_popup_blocked: function(scope, popup_window){
            if ((popup_window.innerHeight > 0)==false){ scope._displayError(); }
        },
        _displayError: function(){
            alert("Popup Blocker is enabled! Please add this site to your exception list.");
        }
    };

Использование:

var popup = window.open("http://www.google.ca", '_blank');
popupBlockerChecker.check(popup);

Надеюсь, это поможет! :)

10 голосов
/ 08 ноября 2010

Ответ Рича больше не будет работать для Chrome. Похоже, что Chrome фактически выполняет любой Javascript во всплывающем окне. Я закончил проверкой значения screenX 0, чтобы проверить наличие заблокированных всплывающих окон. Я также думаю, что нашел способ гарантировать, что это свойство является окончательным перед проверкой. Это работает только для всплывающих окон в вашем домене, но вы можете добавить обработчик загрузки так:

var myPopup = window.open("site-on-my-domain", "screenX=100");
if (!myPopup)
    alert("failed for most browsers");
else {
    myPopup.onload = function() {
        setTimeout(function() {
            if (myPopup.screenX === 0)
                alert("failed for chrome");
        }, 0);
    };
}

Как многие сообщали, свойство screenX иногда сообщает ненулевое значение для неудачных всплывающих окон, даже после загрузки. Я тоже испытывал это поведение, но если вы добавите проверку после тайм-аута 0 мс, свойство screenX всегда будет выдавать непротиворечивое значение.

Дайте мне знать, если есть способы сделать этот скрипт более надежным. Кажется, работает для моих целей, хотя.

9 голосов
/ 06 июня 2012

Это сработало для меня:

    cope.PopupTest.params = 'height=1,width=1,left=-100,top=-100,location=no,toolbar=no,menubar=no,scrollbars=no,resizable=no,directories=no,status=no';
    cope.PopupTest.testWindow = window.open("popupTest.htm", "popupTest", cope.PopupTest.params);

    if( !cope.PopupTest.testWindow
        || cope.PopupTest.testWindow.closed
        || (typeof cope.PopupTest.testWindow.closed=='undefined')
        || cope.PopupTest.testWindow.outerHeight == 0
        || cope.PopupTest.testWindow.outerWidth == 0
        ) {
        // pop-ups ARE blocked
        document.location.href = 'popupsBlocked.htm';
    }
    else {
        // pop-ups are NOT blocked
        cope.PopupTest.testWindow.close();
    }

externalHeight и outerWidth предназначены для Chrome, потому что трюк about: blank сверху не работает в Chrome.

4 голосов
/ 07 февраля 2015

Я собираюсь просто скопировать / вставить ответ, приведенный здесь: https://stackoverflow.com/a/27725432/892099 от DanielB. работает на Chrome 40 и это очень чисто. никаких грязных взломов или ожиданий.

function popup(urlToOpen) {
  var popup_window=window.open(urlToOpen,"myWindow","toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=yes, resizable=yes, copyhistory=yes, width=400, height=400");            
  try {
    popup_window.focus();   
  }
  catch (e) {
    alert("Pop-up Blocker is enabled! Please add this site to your exception list.");
  }
}
2 голосов
/ 01 октября 2018

Как насчет Promise подхода?

const openPopUp = (...args) => new Promise(s => {
  const win = window.open(...args)
  if (!win || win.closed) return s()
  setTimeout(() => (win.innerHeight > 0 && !win.closed) ? s(win) : s(), 200)
})

И вы можете использовать его как классический window.open

const win = await openPopUp('popuptest.htm', 'popuptest')
if (!win) {
  // popup closed or blocked, handle alternative case
}

Вы можете изменить код так, чтобы он не выполнял обещание вместо возврата undefined, я просто подумал, что if был более простой поток управления, чем try / catch для этого случая.

2 голосов
/ 29 августа 2014

Ух ты, тут наверняка есть много решений. Это моё решение, оно использует решения, взятые из текущего принятого ответа (который не работает в последней версии Chrome и требует обтекания его тайм-аутом), а также связанное решение для этой темы (это на самом деле ванильный JS, а не jQuery).

Мой использует архитектуру обратного вызова, которая будет отправлена ​​true, когда всплывающее окно заблокировано, и false в противном случае.

window.isPopupBlocked = function(popup_window, cb)
{
    var CHROME_CHECK_TIME = 2000;       // the only way to detect this in Chrome is to wait a bit and see if the window is present

    function _is_popup_blocked(popup)
    {
        return !popup.innerHeight;
    }

    if (popup_window) {
        if (popup_window.closed) {
            // opened OK but was closed before we checked
            cb(false);
            return;
        }
        if (/chrome/.test(navigator.userAgent.toLowerCase())) {
            // wait a bit before testing the popup in chrome
            setTimeout(function() {
                cb(_is_popup_blocked(popup_window));
            }, CHROME_CHECK_TIME);
        } else {
            // for other browsers, add an onload event and check after that
            popup_window.onload = function() {
                cb(_is_popup_blocked(popup_window));
            };
        }
    } else {
        cb(true);
    }
};
2 голосов
/ 02 ноября 2010

Этот фрагмент включает в себя все вышеперечисленное - по какой-то причине - StackOverflow исключает первую и последнюю строки кода в блоке кода ниже, поэтому я написал блог об этом. Для полного объяснения и остальной части (загружаемого) кода посмотрите на мой блог на thecodeabode.blogspot.com

var PopupWarning = {

    init : function()
    {

        if(this.popups_are_disabled() == true)
        {
            this.redirect_to_instruction_page();
        }
    },

    redirect_to_instruction_page : function()
    {
        document.location.href = "http://thecodeabode.blogspot.com";
    },

    popups_are_disabled : function()
    {
        var popup = window.open("http://localhost/popup_with_chrome_js.html", "popup_tester", "width=1,height=1,left=0,top=0");

        if(!popup || popup.closed || typeof popup == 'undefined' || typeof popup.closed=='undefined')
        {
            return true;
        }

        window.focus();
        popup.blur();

        //
        // Chrome popup detection requires that the popup validates itself - so we need to give
        // the popup time to load, then call js on the popup itself
        //
        if(navigator && (navigator.userAgent.toLowerCase()).indexOf("chrome") > -1)
        {
            var on_load_test = function(){PopupWarning.test_chrome_popups(popup);};     
            var timer = setTimeout(on_load_test, 60);
            return;
        }


        popup.close();
        return false;
    },

    test_chrome_popups : function(popup)
    {
        if(popup && popup.chrome_popups_permitted && popup.chrome_popups_permitted() == true)
        {
            popup.close();
            return true;
        }

        //
        // If the popup js fails - popups are blocked
        //
        this.redirect_to_instruction_page();
    }
};

PopupWarning.init();
2 голосов
/ 21 октября 2010

Вот версия, которая в настоящее время работает в Chrome. Просто небольшое изменение по сравнению с решением Рича, хотя я добавил оболочку, которая также обрабатывает время.

function checkPopupBlocked(poppedWindow) {
 setTimeout(function(){doCheckPopupBlocked(poppedWindow);}, 5000);
}

function doCheckPopupBlocked(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.outerWidth == 0) {
            // This is usually Chrome's doing. The outerWidth (and most other size/location info)
         // will be left at 0, EVEN THOUGH the contents of the popup will exist (including the
         // test function we check for next). The outerWidth starts as 0, so a sufficient delay
         // after attempting to pop is needed.
            result = true;
        }
        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);
        //}
    }

    if(result)
     alert("The popup was blocked. You must allow popups to use this site.");
}

Чтобы использовать это просто сделайте это:

var popup=window.open('location',etc...);
checkPopupBlocked(popup);

Если всплывающее окно заблокировано, по истечении 5-секундного льготного периода появится предупреждающее сообщение (вы можете настроить его, но 5 секунд должно быть вполне безопасным).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...