Обнаружить заблокированное всплывающее окно в 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 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)
});
Надеюсь это поможет.
Только одно улучшение во фрагменте 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, чтобы проверить наличие заблокированных всплывающих окон. Я также думаю, что нашел способ гарантировать, что это свойство является окончательным перед проверкой. Это работает только для всплывающих окон в вашем домене, но вы можете добавить обработчик загрузки так:
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 всегда будет выдавать непротиворечивое значение.
Дайте мне знать, если есть способы сделать этот скрипт более надежным. Кажется, работает для моих целей, хотя.
Это сработало для меня:
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.
Я собираюсь просто скопировать / вставить ответ, представленный здесь: /questions/13169994/kak-ya-mogu-opredelit-blokiruet-li-brauzer-vsplyivayuschee-okno/13170005#13170005 от 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.");
}
}
Как насчет 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
для этого случая.
Этот фрагмент включает в себя все вышеперечисленное - по какой-то причине - Stackru исключает первую и последнюю строки кода в блоке кода ниже, поэтому я написал блог об этом. Для полного объяснения и остальной части (загружаемого) кода загляните в мой блог на 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 и требует его обертки в тайм-аут), а также связанное решение в этом потоке (которое на самом деле является vanilla 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);
}
};
Проверьте положение окна относительно родителя. Chrome заставляет окно появляться почти за кадром.
У меня была похожая проблема, когда всплывающие окна не открывались в 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. Просто небольшое изменение по сравнению с решением Рича, хотя я добавил в обертку, которая также обрабатывает время.
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 секунд должно быть вполне безопасным).
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();
}
};
}
}
ПРИВЕТ
Я немного изменил решения, описанные выше, и думаю, что он работает как минимум для 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>
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();
}
Ответ Джейсона - единственный метод, о котором я тоже могу подумать, но полагаться на такую позицию - немного хитроумно!
В наши дни вам не нужно задавать вопрос "было ли заблокировано мое незапрошенное всплывающее окно?", Потому что ответ неизменно "да" - во всех основных браузерах по умолчанию включен блокировщик всплывающих окон. Наилучший подход - это использовать только window.open() в ответ на прямой щелчок, что почти всегда разрешено.
Насколько я могу судить (из того, что я тестировал), Chrome возвращает объект окна с расположением about:blank. Итак, следующее должно работать для всех браузеров:
var newWin = window.open(url);
if(!newWin || newWin.closed || typeof newWin.closed=='undefined' || newWin.location=='about:blank')
{
//POPUP BLOCKED
}