避免浏览器弹出窗口拦截器

我正在开发一个纯JavaScript的OAuth身份验证流程,我想在弹出窗口中向用户显示“授予访问权限”窗口,但它被阻塞了。

如何防止由window.openwindow.showModalDialog创建的弹出窗口被不同浏览器的弹出窗口阻止程序阻止?

277257 次浏览

一般规则是,如果window.open或类似的东西从javascript中调用,而不是直接用户操作调用,弹出窗口阻止程序将参与。也就是说,你可以调用window.open来响应按钮点击而不被弹出窗口阻止器击中,但如果你在计时器事件中放入相同的代码,它将被阻止。调用链的深度也是一个因素——一些较老的浏览器只查看即时调用者,较新的浏览器可以回溯一点,看看调用者的调用者是否是鼠标点击等。保持它尽可能浅,以避免弹出窗口拦截器。

从谷歌的oauth JavaScript API:

http://code.google.com/p/google-api-javascript-client/wiki/Authentication

请看上面写着:

设置认证

OAuth 2.0的客户端实现使用一个弹出窗口来提示用户登录并批准应用程序。第一次调用gapi.auth.authorize可以触发弹出窗口拦截器,因为它间接地打开了弹出窗口。为了防止弹出窗口阻止程序在认证调用时触发,在客户端加载时调用gapi.auth.init(回调)。当库准备进行身份验证调用时,将执行提供的回调。

我猜这与上面的真实答案有关,它解释了如果有立即的反应,它不会触发弹出警报。“gapi.auth.init"让API立即生效。

实际应用

我在npm上使用节点护照和每个提供商的各种护照包创建了一个开源身份验证微服务。我使用了一个标准的重定向方法到第三方,给它一个重定向URL回来。这是程序化的,所以我可以有不同的地方重定向回如果登录/注册和特定的页面。

github.com/sebringj/athu

passportjs.org

基于杰森赛百灵非常有用的技巧,以及涵盖在这里在那里的东西,我为我的情况找到了一个完美的解决方案:

伪代码与Javascript片段:

  1. 立即在用户操作上创建一个空白弹出框

     var importantStuff = window.open('', '_blank');
    

    (用任何你需要的额外选项丰富对window.open的调用。)

    可选:添加一些“等待”;信息的信息。例子:

    a)外部HTML页面:将上面的行替换为

     var importantStuff = window.open('http://example.com/waiting.html', '_blank');
    

    b)文字:在上面的后面增加以下一行:

     importantStuff.document.write('Loading preview...');
    
  2. 在准备就绪时填充内容(例如,当返回AJAX调用时)

     importantStuff.location.href = 'https://example.com/finally.html';
    

    或者,如果你根本不需要它,你可以在这里关闭窗口(例如if ajax request fails -感谢@Goose的评论):

     importantStuff.close();
    

我实际上使用这个解决方案进行邮件重定向,它适用于我所有的浏览器(windows 7, Android)。顺便说一句,_blank位有助于邮件重定向在移动设备上工作。

作为一个好的实践,我认为测验是一个好主意,如果弹出窗口被阻止,并采取行动。你需要了解那个窗口。Open有一个返回值,如果操作失败,该值可能为空。例如,在以下代码中:

function pop(url,w,h) {
n=window.open(url,'_blank','toolbar=0,location=0,directories=0,status=1,menubar=0,titlebar=0,scrollbars=1,resizable=1,width='+w+',height='+h);
if(n==null) {
return true;
}
return false;
}

如果弹出窗口被阻塞,窗口。Open将返回null。所以函数会返回false。

作为一个例子,想象一下直接从任何链接调用这个函数 target="_blank":如果弹出窗口被成功打开,返回 false将阻塞链接动作,否则如果弹出窗口被阻塞, 返回true将允许默认行为(打开新的_blank . properties)

<a href="http://whatever.com" target="_blank" onclick='return pop("http://whatever.com",300,200);' >
这样你会有一个弹出窗口,如果它工作,和一个_blank窗口如果 不是。< / p >

如果弹出框没有打开,你可以:

  • 像例子中那样打开一个空白窗口,然后继续
  • 打开一个假的弹出窗口(页面内的iframe)
  • 通知用户(“请允许本网站弹出窗口”)
  • 打开空白窗口,然后通知用户 李等。< / >

摆脱这种情况最简单的方法是:

  1. 不要使用document.open()。
  2. 而是使用this.document.location.href = location;要加载的url在哪里

例:

<script>
function loadUrl(location)
{
this.document.location.href = location;
}</script>


<div onclick="loadUrl('company_page.jsp')">Abc</div>
这对我来说很有效。 欢呼声< / p >
除了瑞士先生的帖子,在我的情况下,window.open是在一个承诺内启动的,它打开了弹出窗口拦截器,我的解决方案是: 角:< / p >
$scope.gotClick = function(){


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


}
);
};

browserService:

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


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

这就是如何使用承诺响应而不调用弹出窗口阻止程序打开一个新选项卡。

除非回调成功返回,否则我不想创建新页面,所以我对用户单击的模拟这样做:

function submitAndRedirect {
apiCall.then(({ redirect }) => {
const a = document.createElement('a');
a.href = redirect;
a.target = '_blank';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
});
}

我尝试了多种解决方案,但他的解决方案是唯一一个在所有浏览器中都有效的解决方案

let newTab = window.open();

.href = url

我的用例:在我的react应用程序中,在用户单击后端执行API调用。根据响应,打开新选项卡,并将api响应作为参数添加到新选项卡URL(在同一域中)。

在我的用例中,唯一需要注意的是,接收API响应需要1秒钟的时间。因此弹出窗口阻止显示(如果它是活跃的)时,打开URL在一个新的选项卡。

为了避免上述问题,下面是示例代码,

var new_tab=window.open()
axios.get('http://backend-api').then(response=>{
const url="http://someurl"+"?response"
new_tab.location.href=url;
}).catch(error=>{
//catch error
})

总结:创建一个空选项卡(如上面的第1行),当API调用完成时,您可以用url填充选项卡并跳过弹出窗口阻止程序。

< p > @here 我发现它的工作在所有浏览器

window.open(URL) || window.location.assign(URL)

只需使用window.location.href = yourUrl或带有target='_blank'的url即可

window.open()有太多的问题,如果距离用户操作超过一秒,许多浏览器会将其视为弹出窗口并阻止它