在Chrome中检测被阻止的弹窗

106

我知道一些用于在其他浏览器中检测弹出窗口是否被阻止的JavaScript技术(如此问题的答案中所述)。这是基本测试:

var newWin = window.open(url);

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

但这在Chrome中不起作用。当弹出窗口被阻止时,永远不会到达“POPUP BLOCKED”部分。

当然,在某种程度上,测试是有效的,因为Chrome实际上没有阻止弹出窗口,而是在右下角的小型最小化窗口中打开它,列出“已阻止”的弹出窗口。

我想做的是能够判断Chrome的弹出窗口拦截器是否阻止了弹出窗口。 我尽量避免使用浏览器嗅探,而是采用特性检测的方式。 有没有一种方法可以在不使用浏览器嗅探的情况下实现这一点?

编辑:我现在尝试利用newWin.outerHeightnewWin.left和其他类似的属性来完成此操作。 当弹出窗口被阻止时,Google Chrome将所有位置和高度值都返回为0。

不幸的是,即使弹出窗口实际上已经打开了未知的时间,它也会返回相同的值。在某个神奇的时段(在我的测试中为几秒钟),位置和大小信息将返回为正确的值。 换句话说,我仍然无法更接近解决这个问题。 谢谢任何帮助。


InvisibleBacon和Andy最新的解决方案在Chrome 10中无法工作:“failed for chrome”消息出现,即使测试弹出窗口已成功显示。有什么想法吗? - user671590
我认为需要提出一个新的问题,因为这些解决方案似乎只适用于早期版本的Chrome。 - 700 Software
1
@George Bailey 我同意,但是为了明确起见,它们中的一些在当前版本的Chrome(19)中确实有效。安德鲁最初使用outerHeight(或其他人建议的screenX)的想法对我来说运行良好,结合setTimeout方法。但是,是的,在我进行自己的测试之前,尝试理解所有这些答案确实令人困惑。 - regularmike
试试这个:https://dev59.com/5oPba4cB1Zd3GeqPxc8y#31299484 - Vlada
1
这个回答解决了你的问题吗?如何检测浏览器是否阻止弹出窗口? - Michael Freidgeim
显示剩余2条评论
16个回答

69

你所说的“神奇时间”可能是指弹出窗口的DOM已经加载完毕,或者可能是在所有内容(图像、外部CSS等)都加载完毕时。你可以通过向弹出窗口添加一个非常大的图片来轻松测试这一点(首先清除缓存!)。如果你正在使用jQuery(或类似的Javascript框架),你可以使用ready()事件(或类似事件)在检查窗口偏移量之前等待DOM加载。但这样做的危险在于Safari检测的工作方式有冲突:在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"函数是否存在于子窗口中。
补充于2015年6月15日:
我认为处理这个问题的现代方法是使用window.postMessage(),让子窗口通知父窗口窗口已加载。这种方法类似(子级告诉父级它已经加载了),但通信手段已得到改进。我能够从子级进行跨域操作:
$(window).load(function() {
  this.opener.postMessage({'loaded': true}, "*");
  this.close();
});

使用以下代码,父级组件监听此消息:

$(window).on('message', function(event) {     
  alert(event.originalEvent.data.loaded)
}); 
希望这有所帮助。

4
这个有更新吗?看起来不再起作用了……具体在Chrome浏览器中。 - Chris Wagner
我想我找到了一种方法,可以让这个在新版本的Chrome中运行。请查看我的答案获取详细信息。 - InvisibleBacon
隐形培根先生,您的答案在哪里? - Jonnio
2
基本上Chrome有一个bug。虽然它隐藏了弹出窗口,但仍会执行并返回窗口对象 - 因此常规检查无效。这是对我有效的解决方案:var popup = window.open(url);if (popup) { popup.onload = function () { console.log(popup.innerHeight > 0 ? 'open' : 'blocked'); } } else { console.log('blocked'); } 工作示例在这里:http://jsbin.com/uticev/3/ - Remy Sharp
1
这个答案已经不正确了,请将其更改为@Predrag Stojadinović的答案。 - Lucas B
显示剩余4条评论

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时,我会使用$(myPopup).ready()代替onload。在本地运行时,我的IE速度太快了,"onload"已经发生了。 - Matt Connolly

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),通知用户存在问题并提供可点击的链接,因为它是一个直接链接,可以绕过大多数阻止程序。感谢分享! - Tyler Forsythe
@TylerForsythe,你有关于你的解决方案更多的信息吗?很想能够提供一个直接可点击的链接到内容。 - Joshua Dance
1
@JoshuaDance,这是我刚刚创建的要点,以演示我的修改代码和如何调用它。希望对您有所帮助!https://gist.github.com/tylerforsythe/452ceaad62f507d7cb7bd7ddbffe650c - Tyler Forsythe

10

对于Chrome浏览器,Rich的答案已经不再适用了。看起来现在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”属性有时会在加载完成后对失败的弹出窗口报告非零值。我也遇到了这种情况,但如果你在等待零毫秒之后再检查,screenX属性似乎总是输出一致的值。

如果有方法可以使这个脚本更加稳定,请告诉我。不过对于我的目的来说,它似乎已经能够正常工作了。


对我来说不起作用,onload从未触发。 - user1636522

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

outerHeight 和 outerWidth 适用于 Chrome 浏览器,因为上面提到的“about:blank”技巧在 Chrome 中已经失效。


1
Chrome的变化你发现得很及时,感谢你在这里进行了更新。你的答案应该被标记为正确答案。 - Lucas B
outerWidth和outerHeight在Chrome中也不再起作用了。 - Roman

5

我将直接复制粘贴这里提供的答案:https://dev59.com/0XVD5IYBdhLWcg3wXaYd#27725432,作者是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.");
  }
}

4
怎么样使用 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
}

您可以更改代码,使其失败 promise 而不是返回 undefined,我只是认为这个情况下 iftry / catch 更容易控制流程。


这适用于检测Chrome扩展程序广告拦截器。+1 - Micheal C Wallas

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上可以正常运行的版本。与Rich的解决方案只有稍微的改动,但我增加了一个包装器来处理时间。

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

我在Chrome中遇到了弹出窗口无法打开的类似问题。我感到沮丧,因为我并没有尝试做什么狡猾的事情,比如加载时弹出窗口,只是在用户点击时打开一个窗口。我倍感沮丧的是,当我从firebug命令行运行包含window.open()的函数时可以正常工作,而实际点击我的链接却不行!这是我的解决方案:

错误的方式:从事件监听器(在我的情况下,dojo.connect到DOM节点的onclick事件方法)中运行window.open()。

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

正确的方式:将一个函数分配给调用window.open()方法的节点的onclick属性。

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

当然,如果需要,我仍然可以为相同的onclick事件添加事件监听器。通过这个更改,即使Chrome设置为“不允许任何网站显示弹出窗口”,我也可以打开我的窗口。太好了。

如果有了解Chrome的智者能告诉我们为什么会有所不同,我很乐意听取,尽管我怀疑这只是试图关闭恶意编程弹出窗口的一种尝试。


感谢分享你的解决方案。它很有效。这是在Chrome中打开弹出窗口的最佳和最干净的方法。你的答案应该排在首位。其余的解决方案只是“肮脏”的黑客技巧。 - Mandeep Janjua

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接