【问题标题】:Avoid browser popup blockers避免浏览器弹出窗口拦截器
【发布时间】:2011-02-04 23:19:21
【问题描述】:

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

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

【问题讨论】:

  • 即使有可能(我不知道),如果人们使用弹出窗口拦截器,你应该尊重它。大多数浏览器在站点尝试打开弹出窗口时会显示一条消息,因此如果他们愿意,他们仍然可以看到它。您可以在您的网站上备注某些内容会在弹出窗口中打开,并且用户应该允许它才能继续。
  • 最佳实践如下:1) 成功地做到这一点 2) 关上你的窗户,关上你的门,害怕聚集在一起的不安的网络顾客 3) 忏悔,移除弹出- up-busta-busta,并尊重您的观众。
  • Alex 和 Felix,我已经更新了问题。我不会将知识用于邪恶:)。谢谢!
  • 我想补充一点,绕过弹出窗口拦截器实际上可能是为了让用户体验更好。在我现在正在处理的一个示例中,我们正在使用一个 Javascript 应用程序(基于 ExtJS)并且我们试图让用户使用贝宝付款。我们为他们提供了一个按钮,他们可以单击以在新窗口中启动 paypal,但某些版本的 IE 将其阻止为弹出窗口(即使它是单击按钮)。如果他们现在启用弹出窗口,屏幕重新加载并且作为 JavaScript 应用程序,我们将失去窗口状态,他们必须重新开始。真的:问题是 IE 很笨。
  • @FelixKling 是的,这是可能的。浏览器制造商已经考虑到这一点。只要有用户意图(由用户单击链接或按钮发出信号),就可以打开弹出窗口。弹出窗口拦截器应尊重用户意图。如果 IE 的弹出窗口阻止程序没有,则错误是弹出窗口阻止程序。用户使用弹出窗口阻止程序来阻止脚本随意打开弹出窗口,而不是阻止他们自己尝试打开的弹出窗口(通过单击按钮或链接)。

标签: javascript popup modal-dialog popup-blocker


【解决方案1】:

只需使用window.location.href = yourUrl 或带有target='_blank' 的网址

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

【讨论】:

    【解决方案2】:

    @这里 我发现它在所有浏览器中都可以正常工作

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

    【讨论】:

      【解决方案3】:

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

      带有 Javascript sn-ps 的伪代码:

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

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

        (用whatever additional options you need丰富对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 位有助于 mailto 重定向在移动设备上工作,顺便说一句。

      【讨论】:

      • ?那么如果不是来自浏览器中的用户操作,你会怎么做?对于我的示例,用户对服务器进行身份验证后,它必须打开一个新选项卡
      • Whatever it is you intend to do, there is a better way than a popup window 哈哈?奇怪的概括。客户希望他们验证的页面保持打开状态,并希望验证后的新站点/登录页面在新选项卡中打开。是的,不是我认为出色的用户体验……但这似乎是合理的
      • @mmcrae。认真地坐下来思考。我向您保证,您会在您描述的场景中找到“用户操作”。我的回答的重点是您创建弹出窗口当​​您有用户操作时 - 然后稍后用内容填充它。提示您的情况:用户点击“身份验证”-> 创建弹出窗口(空);计时器已启动或服务器响应返回或其他任何内容 -> 将内容填充到弹出窗口中。
      • 我非常感谢这个解决方案。它工作正常。改进此解决方案的一种方法是在加载页面上添加一个脚本,该脚本会在几秒钟后自动关闭。对于我的用例,这是更好的行为。谢谢!
      • 实用提示,如果ajax请求失败,调用importantStuff.close关闭新标签页并在原页面提供并提醒。
      【解决方案4】:

      我的用例:在我的 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 填充选项卡并跳过弹出窗口阻止程序。

      【讨论】:

        【解决方案5】:

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

        1. 不要使用 document.open()。
        2. 改为使用 this.document.location.href = location;其中location是要加载的url

        例如:

        <script>
        function loadUrl(location)
        {
        this.document.location.href = location;
        }</script>
        
        <div onclick="loadUrl('company_page.jsp')">Abc</div>
        

        这对我来说效果很好。 干杯

        【讨论】:

        • 在新标签页打开网址怎么样?
        【解决方案6】:

        除了瑞士先生的帖子,在我的例子中 window.open 是在一个承诺中启动的,它打开了弹出窗口阻止程序,我的解决方案是: 角度:

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

        这是您可以使用承诺响应而不调用弹出窗口阻止程序来打开新标签的方法。

        【讨论】:

        • 这导致了我的解决方案!我在其中创建了一个包含打开的选项卡的变量,然后在加载数据后填充了 url。谢谢。 const tab = window.open(); observable.subscribe(dataUrl =&gt; tab.location.href = dataUrl);
        • 如果您启用了弹出窗口阻止程序,这将无法解决弹出窗口阻止程序的问题...
        • @Alejandro Vales jonas 解决方案仅在代码从 onClick 事件或任何用户交互运行时才有效。当您尝试通过 windows.open 在承诺中打开一个新选项卡时,超时,订阅...浏览器认为这是对用户的操纵,因此解决方案是在输入承诺之前创建一个实例,您只需更改像乔纳斯一样的href
        • @David 非常感谢!!!我没有碰巧看到.then然后回答,这就是为什么我这么困惑:/ SRY ...
        • @David 这太棒了!奇迹般有效。如果我可以再问一个问题——你知道如何在 Edge 中进行这项工作吗?轻推到正确的资源将极大地帮助...
        【解决方案7】:

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

        let newTab = window.open(); newTab.location.href = url;

        【讨论】:

        • 这对启用了弹出窗口阻止程序的人不起作用
        • 什么是url?它没有被分配。
        • @shinriyo url 是指向您要访问的任何页面的链接,例如http://example.com
        • @AlejandroVales 我启用了弹出窗口阻止程序,它可以工作。问题是window.open() 无法以编程方式打开新窗口,如果我们需要,该答案是选项之一。使用newTab 变量,我们引用window.open(),稍后我们可以调用它,插入我们需要的url,在我的例子中是一些blob 的url,新选项卡将在输入的url 上打开。
        【解决方案8】:

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

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

        【讨论】:

        • 在 Chrome 62 中对我不起作用。弹出窗口(页面)被阻止。
        • 是的,你是对的 - 我也发现这不一致。我最终使我的 api 调用同步
        【解决方案9】:

        来自 Google 的 oauth JavaScript API:

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

        查看上面写着的区域:

        设置身份验证

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

        我猜它与上面的真实答案有关,它如何解释是否有立即响应,它不会触发弹出警报。 “gapi.auth.init”正在制作它,因此 api 立即发生。

        实际应用

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

        github.com/sebringj/athu

        passportjs.org

        【讨论】:

          【解决方案10】:

          作为一种好的做法,我认为测试一个弹出窗口是否被阻止并采取措施以防万一是个好主意。您需要知道 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。

          举个例子,想象一下直接从任何链接调用这个函数 with target="_blank": 如果弹窗打开成功,返回 false 将阻止链接操作,否则如果弹出窗口被阻止, 返回true 将让默认行为(打开新的_blank 窗口)然后继续。

          <a href="http://whatever.com" target="_blank" onclick='return pop("http://whatever.com",300,200);' >
          

          这样,如果它有效,您将有一个弹出窗口,如果有效,您将有一个 _blank 窗口 不是。

          如果弹窗没有打开,您可以:

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

          【讨论】:

          • 谢谢。成功了!
          【解决方案11】:

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

          【讨论】:

          • 有趣的是,通过绑定到选择元素的更改事件启动的弹出窗口将被阻止(在 Chrome 中,而不是 FF 中),即使该事件是由直接用户操作(如点击)启动的。虽然如果绑定到输入,它们是允许的。奇怪。
          • 没有人说浏览器是一致的。 :P
          • 通过实验我必须明白堆栈深度与弹出窗口拦截器无关。它实际上检查是否在用户操作后 1 秒内调用了 window.open。在 Chrome 46 和 Firefox 42 中测试。
          • 可以在两个浏览器中规避 1 秒超时,允许使用@tj-crowder 在this page 上的回答来允许 无限期 的时间量......有你的 URL '通过 ajax 调用是一些在返回响应之前使用 sleep() 一段时间的 php(或其他)。浏览器将在“加载”页面时挂起。然后在收到响应时触发弹出窗口。但是,在 Chrome 中,您必须在 ajax() 之前添加一个 alert() 才能使这个技巧起作用。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-10-27
          • 1970-01-01
          相关资源
          最近更新 更多