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


103

Мне известны методы 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, когда всплывающее окно заблокировано.

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


Йоав, местоположение отображается одинаково независимо от того, заблокировано всплывающее окно или нет. У кого-нибудь еще есть ответ, который не заставляет пользователя ждать 3,5 секунды?

Последние решения от InvisibleBacon и Andy не работают в Chrome 10: сообщение «сбой для хрома» появляется, даже если тестовое всплывающее окно было успешно отображено. Любая идея?

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

1
@George Bailey Я согласен, но для ясности, некоторые из них работают в текущей версии Chrome (19). Первоначальная идея Эндрю об использовании outerHeight (или screenX, как предлагали другие) отлично работает для меня в сочетании с подходом setTimeout. Но да, попытка разобраться во всех этих ответах действительно сбивала с толку, пока я не провела собственное тестирование.
regularmike

Попробуйте это: stackoverflow.com/a/31299484/1927014
Влада

Ответы:


66

Что ж, «волшебное время», о котором вы говорите, вероятно, наступает, когда 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 секунд для загрузки. В дочернем окне нужно добавить тестовую функцию:

function test () {}

Детектор блокировки всплывающих окон проверяет, существует ли функция «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)
}); 

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


Рич, ты гуру всплывающих окон javascript. Спасибо. Это именно то, что мне было нужно.
Эндрю Энсли,

4
Есть обновления по этому поводу? Кажется, больше не работает ... В частности, в Chrome
Крис Вагнер

Думаю, я нашел способ заставить эту работу работать в новых версиях Chrome. Подробности смотрите в моем ответе.
InvisibleBacon

2
В основном это ошибка в Chrome. Хотя он скрывает всплывающее окно, он все равно выполняется, и вы все равно получаете объект окна обратно, поэтому регулярные проверки не работают. Вот решение, которое сработало для меня: var popup = window.open (url); если (всплывающее окно) {popup.onload = функция () {console.log (popup.innerHeight> 0? 'открыть': 'заблокировано'); }} еще {console.log («заблокировано»); } Рабочий пример здесь: jsbin.com/uticev/3
Реми Шарп,

1
Этот ответ больше неверен, пожалуйста, замените его на ответ @Predrag Stojadinović
Lucas B

16

Всего одно улучшение снайпета 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);
    };
}

Зачем закрывать окно, если всплывающие окна разрешены? Разве это не закрытие всплывающего окна, которое вы хотели открыть в первую очередь?
elemjay19

3
Используя jQuery вместо onload, я бы сделал $ (myPopup) .ready (). Мой IE работал локально слишком быстро, и "загрузка" уже произошла.
Мэтт Коннолли,

12

Ниже приведено решение 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);

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


Это действительно полезно. Спасибо, что поделился.
Сувенду Шекхар Гири

Добро пожаловать, Suvendu, я рад, что вы нашли его полезным! Удачного кодирования! :)
Kevin B

1
Я настроил этот код, чтобы передать URL-адрес, который пытается открыть, или вокруг него. Это позволяет методу _displayError () отображать предупреждение (я использую toastr), уведомляющее пользователя о проблеме и предоставляющее интерактивную ссылку, которая позволяет обойти большинство блокировщиков, поскольку это прямая ссылка. Спасибо, что поделился!!
Тайлер Форсайт

@TylerForsythe у вас есть дополнительная информация о вашем решении? Хотелось бы иметь возможность предоставить прямую ссылку на контент.
Joshua Dance

1
@JoshuaDance Вот суть, которую я только что создал, чтобы продемонстрировать мой измененный код и то, как я его вызываю. Надеюсь, поможет! gist.github.com/tylerforsythe/452ceaad62f507d7cb7bd7ddbffe650c
Тайлер Форсайт,

10

Ответ Рича больше не будет работать для 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 всегда будет выводить согласованное значение.

Сообщите мне, есть ли способы сделать этот сценарий более надежным. Хотя, похоже, работает для моих целей.


Не для меня, onloadникогда не срабатывает.
лист

9

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

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

ВнешниеHeight и outerWidth предназначены для chrome, потому что трюк about: blank, приведенный выше, больше не работает в chrome.


1
Хорошая уловка об изменениях хрома, и спасибо, что обновили его здесь. Ваш ответ должен быть отмечен как правильный.
Lucas B

Внешняя ширина и внешняя высота также больше не работают в Chrome
Роман

5

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

3

Как насчет 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. +1
Майкл К. Валлас

2

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


Я попробую и сообщу вам свои результаты. Спасибо.
Эндрю Энсли,

Google Chrome сообщает о левом и верхнем смещениях как 0, когда всплывающее окно «заблокировано». Я думал, что это мой золотой билет, но нет. Он также сообщает смещения как 0 сразу после фактического открытия. В какой-то волшебный момент в будущем после открытия верхнее и левое смещения сообщаются правильно.
Эндрю Энсли

Проверьте мой пост на способ, который, кажется, гарантирует, что смещения установлены перед проверкой.
Невидимый бекон

2

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

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

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

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

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

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

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


Спасибо, что поделились своим решением. Оно работает. Это лучший и самый простой способ открывать всплывающие окна в Chrome. Ваш ответ должен быть на высоте. Остальные решения - это просто «грязные» хаки.
Мандип Джанджуа

2

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


2

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

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

1

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

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


2
Я знаю лучшие практики и т.д. Но я в ситуации, когда мне нужно выполнить эту задачу. Вот почему я задал этот вопрос, а не «должен ли я?»
Эндрю Энсли

1

ЗДРАВСТВУЙ

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

Я поместил это в раздел своего «главного» окна

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

Всплывающий тест выглядит так:

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

К сожалению, это предполагает, что страница, которую вы пытаетесь открыть, контролируется нами. Мне нужно открыть внешнюю страницу, которую я не контролирую.
Roman

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

0

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

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

местоположение по-прежнему будет «about: blank» даже для всплывающих окон, которые не заблокированы. Я тестировал Chrome v28.0.1500.72
Роман
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.