window.open被浏览器拦截问题

2023-05-04 17:21:35 浏览数 (1)

使用window.open前,需要先知道一个概念:Pop-up blocker(弹窗拦截)

Pop-up blocker(弹窗拦截)

目前,主流浏览器都有弹窗拦截机制,目的是为了阻止网站在非用户操作(如点击操作)时恶意弹出窗口(如弹窗广告、打开新窗口等),影响用户体验。

基于这个目的,浏览器会在用户操作时,允许同步打开弹窗;但对于异步打开弹窗操作,浏览器会判断从用户进行操作到打开弹窗的时间间隔,如果时间间隔超过它允许的时间值,就会启动拦截

参考文档:What are pop-ups

时机

由上述可知,使用window.open的时机,应该是在用户操作(如点击操作)时同步调用

代码语言:javascript复制
// 会被拦截
window.open('https://javascript.info');

// 不会被拦截
button.onclick = () => {
  window.open('https://javascript.info');
};

时间

当异步使用window.open时,就需要考虑与用户进行操作的间隔时间,不同浏览器允许的间隔时间不同,我们以FireFox为例

代码语言:javascript复制
// 会被拦截
button.onclick = () => {
  // 间隔3s打开会被拦截
  setTimeout(() => window.open('http://google.com'), 3000);
};


// 不会被拦截
button.onclick = () => {
  // 间隔2s打开不会被拦截
  setTimeout(() => window.open('http://google.com'), 2000);
};

在不同浏览器中实际测试时间间隔为:

Chrome:小于5s(不包括5s)

FireFox:小于3s(不包括3s)

Edge:小于5s(不包括5s)

Safari:小于1s(不包括1s)

参考文档:Popups and window methods

其他方式打开新窗口

网上也搜到一些使用其他方式打开新窗口的方法,但经过实际测试,在异步打开新窗口的情况下,只要超过了浏览器拦截机制允许的间隔时间,也同样会被拦截。

Chrome中测试测试代码如下:

代码语言:javascript复制
// a标签形式
const windowOpenBlank = (src) => {
  console.log('a标签')
  let a = document.querySelector('#window-open-blank-a') as HTMLLinkElement
  if (!a) {
    a = document.createElement('a')
    a.target = '_blank'
    a.style.cssText = 'display: none'
    document.body.append(a)
  }
  a.href = src;
  a.click();
}

// form表单形式
const windowOpenByForm = (src) => {
  console.log('form submit')
  let $form = document.querySelector('#window-open-blank-form') as HTMLFormElement
  if (!$form) {
    $form = document.createElement('form')
    $form.method = 'GET'
    $form.target = '_blank'
    $form.style.cssText = 'display: none'
    document.body.append($form)
  }
  $form.action = src
  $form.submit()
}

let count = 0
const testOpen = (link) => {
  count   
  // 同步
  if (count === 1) {
    // 允许打开
    console.log('window.open方式同步打开')
    window.open(link, '_blank')
  } else if (count === 2) {
    // 允许打开
    console.log('form表单方式同步打开')
    windowOpenByForm(link)
  } else if (count === 3) {
    // 允许打开
    console.log('a标签方式同步打开')
    windowOpenBlank(link)
    return
  }
  
  // 异步
  setTimeout(() => {
    if (count === 4) {
      // 被拦截
      console.log('window.open方式异步打开')
      window.open(link, '_blank')
    } else if (count === 5) {
      // 被拦截
      console.log('form表单方式异步打开')
      windowOpenByForm(link)
    } else if (count === 6) {
      // 被拦截
      console.log('a标签方式异步打开')
      windowOpenBlank(link)
    } else if (count === 7){
      // 允许跳转
      console.log('window.location.href 异步跳转')
      window.location.href = link
    }
  }, 5000)
}

异步方案

1. 使用window.location.href

通过上例发现,window.location.href是允许进行异步操作的,实际测试将setTimeout间隔时间设为1分钟也是不会被拦截的,所以,如果在只能异步操作,但是又得跳转链接的地方,建议使用window.location.href,比如下载附件等操作

2. 引导弹窗

数据异步请求完成之后,弹出一个引导弹窗,用户点击确认按钮之后使用window.open直接跳转

弹窗广告插件

浏览器一般都会有一些第三方的弹窗广告拦截插件,网上能找的大概原理如下:

  1. 针对特定弹窗广告的selector,插件通过css设置display: none;隐藏弹窗广告
  2. 有的广告是通过cookie控制的,插件会注入cookie进行隐藏弹窗广告
  3. 有的插件允许自定义一些过滤规则

从现有查到的资料来看,异步调用window.open被拦截是浏览器自带的机制,和是否使用广告插件无关

0 人点赞