在 Safari 浏览器中,window.open 有时会被拦截。这通常是因为Safari对弹出窗口有更严格的控制,尤其是在用户未明确触发的情况下。以下是一些常见原因和解决方法:
原因
- 用户未明确触发:如果
window.open是在异步操作或回调函数中调用的,而不是直接由用户事件(如点击按钮)触发的,Safari 会拦截弹出窗口。 - 弹出窗口阻止设置:Safari 可能启用了阻止弹出窗口的设置。
- 跨域问题:在某些情况下,跨域操作可能导致弹出窗口被拦截。
解决方法
确保 window.open 是在用户明确触发的事件中调用,例如点击事件。以下是详细的代码示例和解释:
示例 1:直接在事件处理器中调用 window.open
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Window Open Example</title>
</head>
<body>
<button id="openWindowButton">Open Window</button>
<script>
document.getElementById('openWindowButton').addEventListener('click', function() {
window.open('https://www.example.com', '_blank');
});
</script>
</body>
</html>
在这个示例中,window.open 是在用户点击按钮时直接调用的,这样 Safari 通常不会拦截。
示例 2:在异步操作中调用 window.open
如果需要在异步操作中调用 window.open,可以通过在用户事件中预先创建一个窗口,然后在异步操作完成后修改窗口内容。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Window Open Example</title>
</head>
<body>
<button id="openWindowButton">Open Window</button>
<script>
document.getElementById('openWindowButton').addEventListener('click', function() {
// 预先创建一个空窗口
const newWindow = window.open('', '_blank');
// 模拟异步操作
setTimeout(function() {
// 异步操作完成后修改窗口内容
newWindow.location.href = 'https://www.example.com';
}, 1000);
});
</script>
</body>
</html>
在这个示例中,首先在用户点击时创建一个空窗口,然后在异步操作完成后修改窗口的内容。
示例 3:处理跨域问题
如果涉及跨域操作,确保目标URL是允许的,并且浏览器没有阻止该操作。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Window Open Example</title>
</head>
<body>
<button id="openWindowButton">Open Window</button>
<script>
document.getElementById('openWindowButton').addEventListener('click', function() {
// 预先创建一个空窗口
const newWindow = window.open('', '_blank');
// 检查新窗口是否成功创建
if (newWindow) {
// 模拟异步操作
setTimeout(function() {
// 异步操作完成后修改窗口内容
newWindow.location.href = 'https://www.example.com';
}, 1000);
} else {
alert('Popup blocked! Please allow popups for this website.');
}
});
</script>
</body>
</html>
在这个示例中,添加了一个检查,如果新窗口未能成功创建,提示用户允许弹出窗口。
为了确保 window.open 在 Safari 中正常工作,关键是确保它在用户明确触发的事件(如点击)中直接调用。如果涉及异步操作,通过这些方法,可以有效避免 Safari 拦截 window.open。
上面问题中, 预先创建一个空窗口,然后在异步操作完成后修改窗口内容,是网上一个通用的解决办法,但由于是先跳转通常需要提供成功和失败的界面,成本高,不完全通用。
大多数情况下是点击后由接口获取决定的url或者是否跳转,可以用如下封装解决
/**
* 通用 异步 window.open 解决方案
* @param {Object} options
* options.callBack 包含异步的回调 必传
* 使用示例 open()中是包含异步的逻辑
link.handlePushAsync(async (cb) => {
const url = await this.open();
cb(url, {from: this.from})
});
*/
async function handlePushAsync(callBack) {
if (!callBack || typeof callBack !== 'function') return;
let urlInit;
let fromParamsInit = {};
// 兼容性处理:如果浏览器不支持 requestAnimationFrame,则使用 setTimeout
const requestAnimFrame = window.requestAnimationFrame || function (callback) {
return setTimeout(callback, 16); // 16ms 大约是 60fps
};
const cancelAnimFrame = window.cancelAnimationFrame || clearTimeout;
const step = () => {
if (urlInit) {
link.push(urlInit, fromParamsInit);
cancelAnimFrame(frame);
return;
}
requestAnimFrame(step);
};
const frame = requestAnimFrame(step);
callBack((url, fromParams) => {
urlInit = url;
fromParamsInit = fromParams;
});
}
注意该方法在接口返回时间过长还是会拦截,但胜在直接解决问题,正常情况下都是好用的