Обнаружение заблокированных всплывающих окон в 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);
    }
};
Pospi
источник
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();
            } 
        };
    }
}
Syed
источник
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
Роман