如何检测浏览器是否阻止弹出窗口?

162

有时候,我会遇到一个网页试图弹出新窗口(用于用户输入或者某些重要信息),但是弹出窗口阻止程序(popup blocker)阻止了这个行为。

调用窗口有哪些方法可以确保新窗口的正常打开?

9个回答

201

如果您使用JavaScript打开弹出窗口,可以像这样使用:

var newWin = window.open(url);             

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

1
这是一个针对Chrome浏览器的答案:在Chrome中检测被阻止的弹出窗口 - ajwaka
2
@ajwaka,您可以请明确一下这个答案是否针对Chrome不适用,还是说其他答案更适合Chrome?谢谢! - Crashalot
4
今天至少在Chrome上,这段代码似乎能够正常运行,因此提出了问题。 - Crashalot
4
@Crashalot - 你在问我六年前我回答过的事情,但很遗憾我已经不记得那个情况了,同时浏览器在这六年间也有了很大的改进。 - ajwaka

52

我尝试了上面几个示例,但无法在Chrome上使用它们。这种简单的方法似乎可以在Chrome 39、Firefox 34、Safari 5.1.7和IE 11上使用。下面是我们JS库中的代码片段。

openPopUp: function(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.");
    }
}

1
@Surendra,请问您所说的“不起作用”是指什么?是在解析时出现错误吗?还是在运行时出现错误?弹出窗口打开但被标记为已阻止?弹出窗口被阻止但被标记为已打开?我看不出为什么浏览器会失败,除非在这种情况下允许对NULL调用focus(),那么在尝试之前进行NULL测试就可以解决问题。 - FrancescoMM
IE总是返回null,即使弹出窗口成功。 - Devin Garner

35
更新: 弹出窗口存在很久了。最初的想法是在不关闭主窗口的情况下显示另一种内容。现在有其他方法可以实现:JavaScript能够发送对服务器的请求,所以弹出窗口很少使用。但有时它们仍然很方便。
过去,邪恶的网站经常滥用弹出窗口。一个坏页面可能会打开大量带广告的弹出窗口。因此,现在大多数浏览器都试图阻止弹出窗口并保护用户。
如果弹出窗口在onclick等用户触发事件处理程序之外被调用,大多数浏览器会阻止它们。
如果您仔细思考一下,这有点棘手。如果代码直接在onclick处理程序中,则很容易。但是,如果弹出窗口在setTimeout中打开呢?
尝试这段代码:
 // open after 3 seconds
setTimeout(() => window.open('http://google.com'), 3000);

弹出窗口在Chrome中打开,但在Firefox中被阻止。

...而这也适用于Firefox:

 // open after 1 seconds
setTimeout(() => window.open('http://google.com'), 1000);

区别在于Firefox认为2000毫秒或更短的超时是可以接受的,但在此之后,它会删除“信任”,假设现在它是“用户行为”之外。因此,第一个被阻止,而第二个则没有。

This solution for popup blocker checking has been tested in FF (v11), Safari (v6), Chrome (v23.0.127.95) & IE (v7 & v9). Update the displayError function to handle the error message as you see fit.

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.");
    }
};

Usage:

var popup = window.open("http://www.google.ca", '_blank');
popupBlockerChecker.check(popup);
希望这能有所帮助! :)

29
我认为你需要更多的下划线。 - Jacob Stamm
在Firefox中,这会显示一个空白页面,甚至看不到弹出窗口拦截器消息。如何关闭空白页面? - user460114
请注意,如果弹出窗口的URL是可下载的文件(响应标头中包含Content-Disposition:attachment;),则弹出窗口将立即关闭,因此setTimeout代码将失败,浏览器将抱怨“启用了弹出窗口拦截器!”。 对于Chrome浏览器,我建议使用@DanielB的解决方案,它非常好用。 - wonsuc
1
@wonsuc 当打开一个带有 Content-Disposition: attachment; 头的链接时,您不需要在弹出窗口中打开它。只需直接链接到您想要下载的资源,浏览器的本机行为将允许您在不将您重定向离开当前页面的情况下进行下载。 - Kevin B
@KevinB 我必须说我处于一个特殊的情况。我需要下载多个从HTML生成PDF文件的文件。如果HTML内容很大,有时需要几秒钟的时间。如果我使用window.location,如果第一个文件生成时间太长,在第二个请求开始时它将被忽略。这就是为什么我必须使用window.open的原因,因为window.location永远不会让我知道响应何时完成。 - wonsuc

18

无论浏览器厂商或版本如何,始终有效的“解决方案”是,在控件附近的屏幕上放置一个警告信息,礼貌地提醒用户该操作需要弹出窗口,请启用弹出窗口。

我知道这不是什么高级技巧,但它非常简单,只需要大约5分钟的测试,然后您就可以继续处理其他问题了。

一旦用户允许您的站点弹出窗口,如果您不过度使用弹出窗口,那也是非常体贴的。最后一件事情是惹恼您的访问者。


2
不幸的是,有时候这个问题中的所有其他解决方案都不可接受 - 只有这一个。为什么?因为它们都试图显示一个弹出窗口 - 而不仅仅是检测弹出窗口是否启用。如果我想要静默地执行该操作怎么办? - Sagi Mann
这里需要记住的是,用户并不总是能够为您的网站启用弹出窗口。例如,在旧版Safari中,用户只能完全启用或禁用弹出窗口拦截器,没有白名单可用。 - Jeremy

1
我结合了@Kevin B和@DanielB的解决方案。
这样会简单得多。
var isPopupBlockerActivated = function(popupWindow) {
    if (popupWindow) {
        if (/chrome/.test(navigator.userAgent.toLowerCase())) {
            try {
                popupWindow.focus();
            } catch (e) {
                return true;
            }
        } else {
            popupWindow.onload = function() {
                return (popupWindow.innerHeight > 0) === false;
            };
        }
    } else {
        return true;
    }
    return false;
};

使用方法:

var popup = window.open('https://www.google.com', '_blank');
if (isPopupBlockerActivated(popup)) {
    // Do what you want.
}

3
isPopupBlockerActivated函数在return (popupWindow.innerHeight > 0) === false;时永远不会返回。当您的浏览器不是Chrome时,该函数将返回false。因为onload是异步过程。您的函数返回false,然后执行onload,但其结果从未返回。 - Onur Gazioğlu

1
我尝试了很多解决方案,但唯一一个也与uBlock Origin兼容的是利用超时检查弹出窗口的关闭状态。
function popup (url, width, height) {
    const left = (window.screen.width / 2) - (width / 2)
    const top = (window.screen.height / 2) - (height / 2)
    let opener = window.open(url, '', `menubar=no, toolbar=no, status=no, resizable=yes, scrollbars=yes, width=${width},height=${height},top=${top},left=${left}`)

    window.setTimeout(() => {
        if (!opener || opener.closed || typeof opener.closed === 'undefined') {
            console.log('Not allowed...') // Do something here.
        }
    }, 1000)
}

显然,这是一种hack解决方案;就像所有解决此问题的解决方案一样。

您需要在setTimeout中提供足够的时间来考虑初始打开和关闭,因此它永远不会完全准确。这将是一个试错的位置。

将其添加到您的尝试列表中。


1

如果您拥有子代码,一个简单的方法是在其HTML中创建一个简单的变量,如下所示:

    <script>
        var magicNumber = 49;
    </script>

然后从父元素中检查其存在,类似以下方式:

    // Create the window with login URL.
    let openedWindow = window.open(URL_HERE);

    // Check this magic number after some time, if it exists then your window exists
    setTimeout(() => {
        if (openedWindow["magicNumber"] !== 32) {
            console.error("Window open was blocked");
        }
    }, 1500);


我们等待一段时间以确保网页已经加载,并检查它的存在。 显然,如果窗口在1500毫秒后仍未加载,则该变量仍将是undefined

把这个放在窗口加载处理程序上怎么样? - bhansa
1
@bhansa我们无法这样做,因为检查是在父窗口上进行的,而magicNumber变量驻留在子窗口上。 - Lalit Umbarkar

-1

通过使用onbeforeunload事件,我们可以如下检查:

    function popup()
    {
        var chk=false;
        var win1=window.open();
        win1.onbeforeunload=()=>{
            var win2=window.open();
            win2.onbeforeunload=()=>{
                chk=true;
            };
        win2.close();
        };
        win1.close();
        return chk;
    }

它将在后台打开2个黑色窗口

该函数返回布尔值。


-1
对于某些弹出窗口拦截器,这可能无效,但我将其用作基本解决方案并添加了 try {} catch。

try {
        const newWindow = window.open(url, '_blank');
        if (!newWindow || newWindow.closed || typeof newWindow.closed == 'undefined')         {
            return null;
        }

        (newWindow as Window).window.focus();
        newWindow.addEventListener('load', function () {
            console.info('Please allow popups for this website')
        })
        return newWindow;
    } catch (e) {
        return null;
    }

这将尝试调用addEventListaner函数,但如果弹出窗口未打开,则会中断并进入catch,然后询问它是否为对象或null并运行其余代码。


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