
本文介绍一种临时性技术方案:通过拦截 beforeunload 事件并跳转至返回 HTTP 204 的端点,强制暂停 CMS 自动生成的 location.href = "..." 重定向;同时配合图片 onerror 监听与计数机制,实现“等待全部图片加载失败后再继续执行”的控制流。
本文介绍一种临时性技术方案:通过拦截 `beforeunload` 事件并跳转至返回 http 204 的端点,强制暂停 cms 自动生成的 `location.href = "..."` 重定向;同时配合图片 `onerror` 监听与计数机制,实现“等待全部图片加载失败后再继续执行”的控制流。
在实际前端集成场景中,有时需在 CMS 生成的不可修改脚本(如强制跳转)执行前,完成一组异步资源状态检测——例如:批量预加载多个故意失效的追踪图片(src 指向无响应 PHP 脚本),并确保全部触发 onerror 后,才允许后续重定向发生。由于无法修改 <script> 标签内的 window.location.href = "..." 行,传统同步阻塞(如 while(!loaded))会冻结主线程、导致页面无响应,且违反浏览器事件循环机制,因此必须采用异步协调 + 导航拦截的组合策略。</script>
✅ 核心思路分两步
- 拦截即将发生的重定向:利用 beforeunload 事件,在 CMS 脚本执行 location.href = ... 的瞬间,将其“劫持”为一次无副作用的 204 导航,从而取消原跳转;
- 监听图片错误并恢复导航:为每个 Image 实例绑定 onerror,统计失败数量;当全部图片(如 33 个)均触发错误后,再主动执行目标跳转。
? 具体实现代码
<script>
// Step 1: 拦截首次重定向(仅生效一次)
let redirectTarget = null;
window.addEventListener('beforeunload', function handler() {
// 捕获 CMS 即将设置的跳转地址(注意:此时 location.href 尚未变更)
redirectTarget = window.location.href;
// 必须用 setTimeout 延迟执行 —— 直接赋值会被浏览器静默忽略
setTimeout(() => {
// 推荐使用自建 204 端点(更可控、避免依赖 Google)
// 例如:/api/noop?status=204 → 返回 HTTP 204 + 空响应体
window.location.href = '/api/noop?status=204';
}, 1);
// 移除自身,防止干扰用户其他操作(如手动刷新/点击链接)
window.removeEventListener('beforeunload', handler);
}, { once: true });
// Step 2: 创建并监控所有图片
const totalImages = 33;
let errorCount = 0;
function createTrackingImage(url) {
const img = new Image();
img.onerror = () => {
errorCount++;
console.log(`Image failed (${errorCount}/${totalImages}):`, url);
// ✅ 所有图片均失败 → 恢复原始跳转
if (errorCount === totalImages && redirectTarget) {
window.location.href = redirectTarget;
}
};
img.src = url;
}
// 示例:批量创建(替换为你的真实 URL 列表)
const urls = [
'https://www.hootv.lt/bitrix/spread.php?s=QklUUklYX1NNX1VJREgBbjVkZXNEQ2IzVnU4ckRSSmlCbW82R0xKdmNWcUV2UzUBMAEvAQExATECQklUUklYX1NNX1VJREwBMzcwNjU0NTQyMDcBMAEvAQExATECQklUUklYX1NNX1VJREQBMjVwbmJzNjNkY2VrNGRuZ2VmaGEyYXZsMm1taGh6YWMBMTcyNjMzNjUzMAEvAQExATECQklUUklYX1NNX05DQwFZATE3MjYzMzY1MzABLwEBAQJCSVRSSVhfU01fQ0MBTABALwEBAQI%3D&k=c545bd21253d81d854fc3698d4b20854',
// ... 其余 32 个 URL
];
urls.forEach(createTrackingImage);
</script>
⚠️ 关键注意事项
- 204 端点必须自建:https://www.google.com/gen_204 属于外部依赖,存在被屏蔽、限流或策略变更风险。建议部署一个轻量 Node.js/PHP 路由(如 /api/noop),明确返回 HTTP/1.1 204 No Content 且无响应体;
- setTimeout 不可省略:浏览器禁止在 beforeunload 处理函数内直接修改 location,否则静默失败;
- 严格限制拦截范围:{ once: true } 和动态移除监听器是必需的,否则可能意外阻止用户正常浏览行为(如关闭标签页、点击外部链接);
- 非长期方案:该技巧属于浏览器行为“灰色地带”,未来 Chrome/Firefox 可能收紧 beforeunload 权限(如仅允许弹窗提示)。强烈建议将此作为过渡方案,同步推动 CMS 层改造(例如:提供钩子、支持异步前置校验、或改用 fetch() + AbortController 替代图片打点)。
? 总结
本方案不追求“真正阻塞”,而是通过导航拦截 + 状态驱动恢复,在不可修改的 CMS 环境中达成业务目标。它平衡了可行性与风险,适用于紧急上线场景;但工程上应视其为“技术债”,尽快推动架构层面优化,以获得更健壮、可维护的解决方案。










