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


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

var newWin = window.open(url);

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

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

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

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

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

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

16 99

16 ответов:

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

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

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

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:

Я думаю, что современный способ справиться с этим будет использовать окна.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, потому что трюк "about:blank" сверху больше не работает в 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. Я был расстроен, потому что я не пытался сделать что-то подлое, как всплывающее окно onload, просто открывая окно, когда пользователь щелкнул. Я был вдвойне расстроен, потому что запускал свою функцию, которая включала окно.open () из командной строки firebug работал, в то время как на самом деле нажав на мою ссылку не сделал! Вот мое решение:

неправильный путь: работает Окно.откройте () из прослушивателя событий (в моем случае, 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);
    }
};

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

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

привет

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

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

<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 МС innerheight был установлен правильно 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 возвращает объект окна с расположением 'about:blank'. Итак, для всех браузеров должно работать следующее:

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