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

Javascript 避免浏览器弹出窗口拦截器,javascript,popup,modal-dialog,popup-blocker,Javascript,Popup,Modal Dialog,Popup Blocker,我正在开发纯JavaScript的OAuth身份验证流,我想在弹出窗口中向用户显示“授权访问”窗口,但它被阻止了 如何防止由window.open或window.showModalDialog创建的弹出窗口被不同浏览器的弹出窗口阻止程序阻止?一般规则是,如果window.open或类似程序是从未被直接用户调用的javascript中调用的,弹出窗口阻止程序将启用行动。也就是说,您可以调用窗口。打开以响应按钮单击,而不会被弹出窗口阻止程序击中,但是如果您在计时器事件中放入相同的代码,它将被阻止。调

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


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

一般规则是,如果
window.open
或类似程序是从未被直接用户调用的javascript中调用的,弹出窗口阻止程序将启用行动。也就是说,您可以调用
窗口。打开
以响应按钮单击,而不会被弹出窗口阻止程序击中,但是如果您在计时器事件中放入相同的代码,它将被阻止。调用链的深度也是一个因素-一些旧浏览器只查看直接调用方,新浏览器可以稍微回溯一点,查看调用方的调用方是否是鼠标点击等。尽可能使其保持浅,以避免弹出窗口阻止程序。

来自谷歌的oauth JavaScript API:

请参见以下区域:

设置身份验证

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

我猜它与上面的真实答案有关,因为它解释了如果有即时响应,它不会触发弹出警报。“gapi.auth.init”使api立即发生

实际应用

我使用npm上的节点passport和每个提供者的各种passport包制作了一个开源身份验证微服务。我对第三方使用了标准的重定向方法,给它一个重定向URL返回。这是编程的,所以我可以在不同的地方重定向回if登录/注册以及特定页面

基于的,以及所涵盖的内容,我为我的案例找到了一个完美的解决方案:

带有Javascript片段的伪代码:

  • 立即在用户操作时创建一个空白弹出窗口

     var importantStuff = window.open('', '_blank');
    
    (丰富对
    窗口的调用。使用打开
    。)

    可选:添加一些“等待”信息消息。示例:

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

     var importantStuff = window.open('http://example.com/waiting.html', '_blank');
    
    b) 文本:在上述行下方添加以下行:

     importantStuff.document.write('Loading preview...');
    
  • 准备好后用内容填充它(例如,当AJAX调用返回时)

    或者,如果您根本不需要它,您可以在这里关闭该窗口(例如,如果ajax请求失败,则关闭该窗口)
  • ——感谢@Goose的评论):


    实际上,我使用这个解决方案进行邮件重定向,它可以在我所有的浏览器(Windows7,Android)上运行。
    \u blank
    位有助于mailto重定向在移动设备上工作,顺便说一句。

    作为一种良好做法,我认为最好测试弹出窗口是否被阻止,并在这种情况下采取措施。您需要知道window.open有一个返回值,如果操作失败,该值可能为null。例如,在以下代码中:

    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;
    }
    
    如果弹出窗口被阻止,window.open将返回null。因此,函数将返回false

    例如,想象一下直接从任何链接调用此函数 使用
    target=“\u blank”
    :如果弹出窗口成功打开,则返回
    false
    将阻止链接操作,否则如果弹出窗口被阻止, 返回
    true
    将使默认行为(opennew\u)为空 (窗户)然后继续

    
    
    这样,如果工作正常,你会有一个弹出窗口,如果工作正常,你会有一个空白窗口 不是

    如果弹出窗口未打开,您可以:

    • 打开一个空白窗口,如示例中所示,然后继续
    • 打开一个假弹出窗口(页面内的iframe)
    • 通知用户(“请允许此站点的弹出窗口”)
    • 打开一个空白窗口,然后通知用户 等等

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

      • 不要使用document.open()
      • 而是使用this.document.location.href=location;其中location是要加载的url
      • 例:

        
        函数loadUrl(位置)
        {
        this.document.location.href=位置;
        }
        Abc
        
        这对我很有效。
        干杯

        此外,瑞士先生邮报,在我的例子中,window.open是在一个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响应打开新选项卡,而不调用弹出窗口阻止程序。

        除非回调成功返回,否则我不想创建新页面,因此我这样做是为了模拟用户单击:

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

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

        让newTab=window.open();
        
        newTab.location.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填充选项卡并跳过弹出窗口阻止程序。

        即使可能(我不知道),如果人们使用弹出窗口阻止程序,您也应该尊重它。大多数浏览器在站点试图打开弹出窗口时都会显示一条消息,以便在需要时仍能看到它。你可以在你的网站上写一句话,一些内容会在弹出窗口中打开,用户会
        $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;
        }
        
        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);
          });
        }
        
        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
        })