如何等待 Ajax 请求结束后打开新窗口而不被浏览器屏蔽?
场景
假设这样一个场景:用户点击一个按钮,此时需要请求后端接口进行数据实时校验,如果校验通过则弹出新窗口并打开某个特定的URL,如果校验失败则给出错误提示并停留在当前页面不做任何处理。
一般情况下,你可能会直觉想到这样去做,但显然问题没有这么简单,尝试后你会发现现代浏览器会屏蔽Ajax回调中的弹出窗口:
$('.button').on('click', function() {
$.ajax({
url: '/',
success: function (data) {
if (data) {
window.open('http://www.baidu.com');
}
}
});
});
针对IE浏览器,解决办法很简单,只需要封装一个通用方法,通过创建一个a标签并模拟触发其点击事件即可:
function openwin(url) {
var a = document.createElement("a");
a.setAttribute("href", url);
a.setAttribute("target", "_blank");
a.setAttribute("id", "camnpr");
document.body.appendChild(a);
a.click();
}
openwin('http://www.baidu.com');
但对于Chrome等现代浏览器,这种方法就不那么凑效了:
Modern browsers will allow popups when they're launched from an event loop triggered by an explicit user action. Thus, it's perfectly OK (ignoring web design best practices) to open up something like a "Help" section for your website in a separate window if that happens when the user clicks a "Help Me!" button. Also, it's become quite common for sites to use in-page "pseudo windows" to jam content in front of hapless visitors, and browsers really can't do anything to stop that.
这是因为现代浏览器会屏蔽非用户明确触发的弹出窗口,但还是有Hack方法可以解决,在按钮点击后执行setTimeout并延时打开新窗口,之后根据Ajax的结果去判断是否clearTimeout:
$('.button').on('click', function() {
setTimeout(function() {
window.open('http://www.baidu.com')
}, 1000);
});
以上只是介绍下思路,以下是更完整的解法和代码:
$('.btn').on('click', function() {
var ajax = new $.Deferred();
$.ajax({
url: '/',
success: function (data) {
if (data) {
ajax.resolve();
}
}
});
openNewWin('http://www.baidu.com', ajax);
});
function openNewWin(url, ajax, waiting) {
var stack = [], current = 0;
waiting = waiting > 0 ? waiting : 5;
for (var i = 1; i < waiting; i++ ) {
stack.push(setTimeout(function() {
if (ajax.state() == "resolved") {
$.each(stack, function (key, val) {
console.log(val);
clearTimeout(val);
});
window.open(url);
} else {
stack.shift();
}
}, i * 1000))
}
}