关于异步window.open被浏览器拦截问题解决

3,006 阅读2分钟

问题描述:

当浏览器检测到非用户操作产生的新弹出窗口,则会对其进行阻止。因为浏览器认为这不是一个用户希望看到的页面。此行为根据不同浏览器和不同版本的有所不同。

场景:

需要在用户点击之后触发一个 下载文件 操作或者 打开新页面 的操作之前,需要调接口来进行判断(如权限、任务执行进度等)

解决方案

  1. 使用同步的ajax请求进行open前判断
    优点:所有浏览器适用,无需兼容处理。
    缺点:会阻塞代码执行,请求pending阶段浏览器处于假死状态;浏览器会报warning;

  2. 使用中转页的方式,先打开新页面,待请求返回之后进行重定向或关闭
    优点:目前使用较多的方式,所有浏览器适用。
    缺点:无论判断是否通过都需要打开新页面;用户感知、提示不够友好

仅适用于下载:

  1. 适用模拟a标签点击配合魔幻的h5的download属性
    优点:.....
    缺点:download属性有兼容问题(canIuse中查) && download不支持跨域的资源v<火狐中实测>
    不使用download的话也都会被拦截(新开target="_blank")

  2. 不使用window.open,使用window.location.href进行下载
    优点:所有浏览器适用
    缺点:不能下载pdf/png等浏览器可以直接打开的资源;后台下载接口不能报错,否则会替换原有页面,体验不友好

代码:

  1. 使用同步的ajax请求进行open前判断
ajax('get', url, false).then(() => {
    window.open('http://www.baidu.com')
})

function ajax(type, url, isAsyac) {
    return new Promise(function (resolve, reject) {
        var xhr = new XMLHttpRequest();
        // 第三个参数确定是否是异步,true 异步
        xhr.open(type, url, isAsyac);
        xhr.onreadystatechange = function () {
            if(xhr.readyState === 4) {
                resolve(xhr.response)
            }
        }
        xhr.send()
    })
}
  1. 使用中转页的方式,先打开新页面,待请求返回之后进行重定向或关闭
var newWin = window.open('loading page')
ajax().then(res => {
	newWin.location.href = 'target url'
}).catch(() => {
	newWin.close()
})
  1. a标签模拟点击
ajax().then(res => {
	asyncOpen(res.url)
})

function asyncOpen(url) {
    var a = document.createElement('a')
    a.setAttribute('href', url)
    a.setAttribute("target", "_blank");
    a.setAttribute("download", 'name');
    document.body.appendChild(a);
    a.click();
    a.remove();
}