Обнаружение заблокированного всплывающего окна в Chrome

Я знаю о методах 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 ответов


Ну, "волшебное время", о котором вы говорите, вероятно, когда DOM всплывающего окна был загружен. Или же это может быть, когда все (изображения, подвесной CSS и т. д.) был загружен. Вы можете легко проверить это, добавив очень большую графику во всплывающее окно (сначала очистите кэш!). Если вы используете структуру Javascript, такую как jQuery (или что-то подобное), вы можете использовать событие ready () (или что-то подобное), чтобы дождаться загрузки DOM, прежде чем проверять смещение окна. Опасность в том, что Обнаружение Safari работает противоречивым образом: DOM всплывающего окна никогда не будет готов() в Safari, потому что он даст вам действительный дескриптор для окна, которое вы пытаетесь открыть, независимо от того, действительно ли оно открывается или нет. (на самом деле, я считаю, что ваш всплывающий тестовый код выше не будет работать для 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 секунд для загрузки. В дочернем окне необходимо добавить тестовую функцию:

тест функция() {}

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

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

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

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

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

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

надеюсь, что это помогает.


только одно улучшение для snipet 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);
    };
}

далее 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);

надеюсь, что это помогает! :)


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

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" иногда сообщает ненулевое значение для сбоя всплывающие окна, даже после загрузки. Я также испытал это поведение, но если вы добавляете проверку после нулевого таймаута ms, свойство screenX всегда выводит согласованное значение.

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


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

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

outerHeight и outerWidth предназначены для chrome, потому что трюк "о:пустой" сверху больше не работает в chrome.


Я собираюсь просто скопировать / вставить ответ, приведенный здесь:https://stackoverflow.com/a/27725432/892099 Даниэль . работает на 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.");
  }
}

Проверьте положение окна относительно родительского. Chrome заставляет окно появляться почти выкл-экран.


у меня была аналогичная проблема с всплывающими окнами, не открывающимися в Chrome. Я был разочарован, потому что я не пытался сделать что-то подлое, как всплывающее окно onload, просто открывая окно, когда пользователь щелкнул. Я был вдвойне расстроен, потому что запуск моей функции, которая включала окно.open () из командной строки firebug работал, в то время как на самом деле нажатие на мою ссылку не работало! Вот мое решение:--3-->

неправильный путь: запуск окна.open () из прослушивателя событий (в моем случае, dojo.подключение к метод события onclick узла DOM).

dojo.connect(myNode, "onclick", function() {
    window.open();
}

правильный путь: назначение функции свойству onclick узла, который вызвал window.открытый.)(

myNode.onclick = function() {
    window.open();
}

и, конечно, я все еще могу делать прослушиватели событий для того же события onclick, если мне нужно. С этим изменением я мог открыть свои окна, даже если Chrome был настроен на "не позволять любому сайту показывать всплывающие окна". Радость.

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


вот версия, которая в настоящее время работает в 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 секунд должны быть вполне безопасными).


этот фрагмент включает в себя все вышеперечисленное - по какой - то причине 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();

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

Mine использует архитектуру обратного вызова, которая будет отправлена 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);
    }
};

ответ Джейсона-единственный метод, который я тоже могу придумать, но полагаться на такую позицию немного изворотливо!

в эти дни вам действительно не нужно задавать вопрос "Было ли мое незапрашиваемое всплывающее окно заблокировано?", потому что ответ неизменно " да " - все основные браузеры имеют блокировщик всплывающих окон включен по умолчанию. Лучший подход-только к окну.open () в ответ на прямой щелчок, который почти всегда разрешен.


привет

Я немного изменил решения, описанные выше, и думаю, что он работает для Chrome, по крайней мере. Мое решение сделано, чтобы определить, заблокировано ли всплывающее окно при открытии главной страницы, а не при открытии всплывающего окна, но я уверен, что есть некоторые люди, которые могут его изменить.:-) Недостатком здесь является то, что всплывающее окно отображается в течение нескольких секунд (возможно, можно немного сократить), когда нет popup-blocker.

Я положил это в моей секции главное окно

<script type="text/JavaScript" language="JavaScript">

 var mine = window.open('popuptest.htm','popuptest','width=1px,height=1px,left=0,top=0,scrollbars=no');
 if(!mine|| mine.closed || typeof mine.closed=='undefined')
  {
    popUpsBlocked = true       
    alert('Popup blocker detected ');
    if(mine)
      mine.close();
 }
 else
 {
    popUpsBlocked = false    
    var cookieCheckTimer = null;
    cookieCheckTimer =  setTimeout('testPopup();', 3500);
 }


function testPopup()
{
  if(mine)
  {
    if(mine.test())
    {
       popUpsBlocked = false;
    }
    else
    {
        alert('Popup blocker detected ');
         popUpsBlocked = true;
     }
    mine.close();
}

} 
</script>

В popuptest выглядит так:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
    <title>Popup test</title>
<script type="text/javascript" language="Javascript">
   function test() {if(window.innerHeight!=0){return true;} else return false;}
</script>
</head>

<body>
</body>
</html>

как я называю тест-функцию на всплывающей странице после 3500 МС внутренняя высота была правильно установлена Chrome.

Я использую переменную popUpsBlocked, чтобы знать, отображаются ли всплывающие окна или нет в других javascripts. я.е

function ShowConfirmationMessage()
{
if(popUpsBlocked)
 { 
  alert('Popups are blocked, can not display confirmation popup. A mail will be sent with the confirmation.');
 } 
 else
 { 
  displayConfirmationPopup();
 }
 mailConfirmation();
}

function openPopUpWindow(format)
{   
    var win = window.open('popupShow.html',
                          'ReportViewer',
                          'width=920px,height=720px,left=50px,top=20px,location=no,directories=no,status=no,menubar=no,toolbar=no,resizable=1,maximize:yes,scrollbars=0');

    if (win == null || typeof(win) == "undefined" || (win == null && win.outerWidth == 0) || (win != null && win.outerHeight == 0) || win.test == "undefined") 
    {
        alert("The popup was blocked. You must allow popups to use this site.");  
    }
    else if (win)
    {
        win.onload = function()
        {          
            if (win.screenX === 0) {
                alert("The popup was blocked. You must allow popups to use this site.");
                win.close();
            } 
        };
    }
}

как о 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 для данного случая.


насколько я могу судить (из того, что я тестировал) Chrome возвращает объект window с местоположением "about:blank". Итак, для всех браузеров должно работать следующее:

var newWin = window.open(url);
if(!newWin || newWin.closed || typeof newWin.closed=='undefined' || newWin.location=='about:blank')
{
    //POPUP BLOCKED
}