如何防止Google Chrome阻止我的弹出窗口?

47
在我的网站上有一个按钮,以前只用调用一个函数来调用window.open,但是最近需要在弹出窗口打开之前进行服务器端检查。
自从添加了执行AJAX调用的代码后,浏览器会阻止在AJAX调用的success回调中打开的弹出窗口。我了解到如果不是由用户点击事件调用的弹出窗口,浏览器可能会阻止弹出,所以我尝试将AJAX请求设置为async: false,这在Firefox中解决了问题,但Google Chrome仍然阻止我的弹出窗口。是否有任何方法可以避免这种情况?
我可以将服务器端检查移动到在弹出窗口中打开的页面上,但如果可能的话,我希望在打开弹出窗口之前进行检查。
代码:
<a id="attackButton" href="#">Attack Base!</a>

<script type="text/javascript">
$(function() {
    $('#attackButton').click(function() {
        $.ajax({
            url: baseurl + '/index.php?option=com_pbbgs&format=raw&getinfo=goingame',
            data: { 'gameid': 618 },
            dataType: 'text',
            async: false,
            type: 'POST',
            success: function(data) {
                eval(data);

                if (window.gameURL) {
                    goingameRaw();
                }
            }
        });

        return false;
    });
});

function goingameRaw()
{
    window.open(window.gameURL,'test','left=20,top=20,width=1024,height=640,toolbar=0,resizable=0,location=0');
}
</script>

示例响应体:

window.gameURL="http://mydomain.com/index.php?option=com_pbbgs&format=raw&startgame=618&width=1024&height=640";checktutorial('js','attack');

1
可能是[避免浏览器弹出窗口阻止程序]的重复问题(https://dev59.com/bHE85IYBdhLWcg3w1HOF)。 - Rémi Becheras
7个回答

51

是的,弹出窗口应该是用户行为的直接结果。在ajax回调中执行它们不会起到作用。此外,使用 async:false 是不好的——在Firefox浏览器中,它被知道会阻塞整个浏览器。考虑其他方法来进行检查:

  • 它可以是您在弹出窗口中执行的第一件事情
  • 您可以在单击时打开弹出窗口,等待回调触发后再对其进行操作
  • 您可以要求用户再次单击某个按钮以触发弹出窗口(可能是最糟糕的解决方案)
  • 您可以在页面加载时执行它

3
我早有所料,谢谢。我采纳了你的第二个建议。 - Aistina
这里有一个很好的例子:http://theandystratton.com/2012/how-to-bypass-google-chromes-javascript-popup-blocker - Barbaros Alp
1
@Emil 为什么数字3是最糟糕的?当用户点击后退按钮或其他操作导致UI与登录状态不同步时,只需单击一次Javascript确认框即可。我要试试看。 - Ian Warburton
是的,我同意,这很糟糕。 - Ian Warburton
@Emil Ivanov。第二个选项对我有效,即先打开窗口,然后进行操作。谢谢。 - Vijay Raju
@Emi lvanov:在我的情况下,第一页显示window.open null,但在另一页中它正确地打开了。我不明白为什么会出现这种情况? - Kapil Soni

23

在Emil的出色回答后进行跟进,"您可以在单击时打开弹出窗口,并在回调触发后稍后操作它"。我使用了这个实现。

$('#attackButton').click(function() {

这里是新代码

    var win = window.open('');
    window.oldOpen = window.open;
    window.open = function(url) { // reassignment function
        win.location = url;
        window.open = oldOpen;
        win.focus();
    }

结束新代码

    $.ajax({
        url: baseurl + '/index.php',
        data: { 'gameid': 618 },
        type: 'POST',
        success: function(data) {
            window.open('some url'); // will call reassignment function above 
        }
    });

    return false;
});

5
没必要添加一个全局变量'oldOpen'并重载本地的全局'open'。这都在同一个闭包中,作用域链非常好。只需创建一个本地函数“myWinGo”或其他名称,并使用它即可。 - Timo Tijhof
1
这对于这个例子来说是一个好观点。然而,如果window.open调用在您不想更改的库中,或者如果您想保留调用作为window.open以便阅读性好,那么覆盖是一个很好的选择。 - gabe
哇,天才的小函数!它甚至恢复了 window.open。 - user3413723
1
当执行var win = window.open('');时,我会得到一个空白的新标签页。理想情况下,我希望新窗口只在ajax成功后打开。 - Benjamin Crouzier
1
@BenjaminCrouzier 很不幸,这种与用户点击无关的弹出窗口似乎是Chrome所阻止的。 - gabe
@BenjaminCrouzier 在调用 win = window.open() 后,您可以调用 window.focus(),然后如果 ajax 失败,就调用 win.close()。这样,用户就不会被弹出窗口所打扰。 - nitzel

5

死链!这种答案在SO上是极不鼓励的,特别是因为这个原因。 - Octopus

2
在我的情况下,window.open 是在angular的一个promise中启动的,这导致了弹出窗口拦截器被打开,我的解决方案是:
$scope.gotClick = function(){

  var myNewTab = browserService.openNewTab();
  someService.getUrl().then(
    function(res){
      browserService. updateTabLocation(res.url, myNewTab);
    }
  );
};

浏览器服务:

this.openNewTab = function(){
  var newTabWindow = $window.open();
  return newTabWindow;
}

this.updateTabLocation = function(tabLocation, tab) {
  if(!tabLocation){
    tab.close();
  }
  tab.location.href = tabLocation;
}

使用promise响应而不调用弹出拦截器,您可以打开新选项卡的方法如下:


2

这是我在网站中使用的一个纯JavaScript 解决方案,用于绕过Chrome浏览器中的弹出窗口拦截器。

假设我们有以下HTML按钮:

<button id="clickMe">Click Me!</button>

现在当用户点击按钮时,你应该首先打开一个空的新窗口。在 Fetch 请求完成后,更新实际窗口的 URL。如果请求失败,只需关闭该窗口:
const button = document.querySelector('#clickMe');

// add click event listener
button.addEventListener('click', () => {

    // open an empty window
    const tab = window.open('about:blank');

    // make an API call
    fetch('https://reqres.in/api/users')
        .then(res => res.json())
        .then(json => {

            // TODO: do something with JSON response

            // update the actual URL
            tab.location = 'https://attacomsian.com';
            tab.focus();
        })
        .catch(err => {
            // close the empty window
            tab.close();
        });
});


1

之前的解决方案对我无效,但我基于它并使用了命名窗口。

internalCounter++;
var tabbedWin = window.open('','windowName_'+internalCounter);
$.ajax({
        url: url,
        async: false,
        indexValue:internalCounter,
        success: function(){
            var w= window.open(url, 'windowName_'+this.indexValue);                
            w.focus();
        }
})

参考 Gabe 的解决方案。 - Gilad F

0
我使用以下方法:
  1. 将下载链接作为参数添加到同一页面的重定向链接中:
window.location.href = window.location.protocol + '//' +
                    window.location.host + window.location.pathname +
                    "?download=" + encodeURIComponent(urlToDownload)

检测页面初始化时的此参数并重定向到下载:
function param(name){
    var results = new RegExp('[\\?&]' + name + '=([^&#]*)').exec(window.location.href);
    if (!results) { return 0; }
    return results[1] || 0;
}

var downloadParam = param('download');
if (downloadParam) {
    window.location = decodeURIComponent(downloadParam);
}

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