是否可阻止JavaScript检测window/document的焦点状态
部分网站在用户切换至其他标签页时会停止相关功能运行(例如暂停音乐播放),核心疑问是客户端是否可以阻止这类行为。
具体疑问点
当前网站检测浏览器标签页焦点状态的常见方式包括:
- 调用
document.hasFocus()方法判断焦点状态 - 通过
window.addEventListener('focus', 回调函数)绑定焦点监听事件 - 监听
visibilitychange、blur事件判断页面可见性与焦点变化
已知visibilitychange、blur事件不可取消,普通场景下调用Event.stopPropagation无法阻断其传播,是否存在其他方式阻止这类事件传递?例如是否可通过浏览器扩展修改blur事件的运行逻辑,阻止该事件派发?
另外,是否可通过JS控制台模拟标签页持有焦点的状态?例如在Web控制台中切换对应配置项,或是在标签页失焦后持续重新触发focus事件?又或是通过Tampermonkey脚本将只读属性document.visibilityState强制固定为visible?
注:本问题与“如何检测浏览器窗口是否处于非活跃状态”并非同一问题,二者提问方向完全相反:后者询问如何检测窗口焦点变化,本问题询问如何规避这类焦点检测。
1. Tampermonkey油猴脚本方案(最推荐,可针对单站点生效)
之前提到Event.stopPropagation无法阻断事件,本质是调用时机晚于页面自身的监听器绑定时机。只要在页面业务代码加载前就注入脚本,在捕获阶段注册最高优先级的事件拦截,配合覆写相关检测API,就能完全绕过网站的焦点检测。
脚本需要将运行时机设置为@run-at document-start,核心参考代码如下:
// 固定visibilityState返回值为visible Object.defineProperty(document, 'visibilityState', { get: () => 'visible', configurable: true }); // 固定document.hidden返回值为false Object.defineProperty(document, 'hidden', { get: () => false, configurable: true }); // 覆写hasFocus方法,固定返回焦点存在状态 Document.prototype.hasFocus = () => true; // 捕获阶段拦截失焦、可见性变化事件,阻止页面业务逻辑接收到对应事件 const blockEvents = ['blur', 'visibilitychange']; blockEvents.forEach(eventName => { window.addEventListener(eventName, (e) => { e.stopImmediatePropagation(); }, true); }); // 定时触发focus事件,兜底绕过部分定时检测逻辑 setInterval(() => { window.dispatchEvent(new Event('focus')); }, 1000);
2. 浏览器控制台临时方案
如果只是临时单次使用,不需要长期生效,可以直接打开对应网站的开发者工具控制台,将上述代码粘贴运行即可。如果浏览器开启了控制台粘贴安全拦截,先在控制台输入allow pasting回车解锁粘贴权限再操作。
注意:该方案仅对当前页面生效,页面刷新后会自动失效。
3. 浏览器扩展方案
如果不想自行编写油猴脚本,也可以使用专门的标签页状态欺骗类扩展,这类扩展的核心逻辑和上述油猴脚本一致,会在页面加载的最早阶段注入对应代码覆写API、拦截事件,安装后给对应站点开启权限即可生效,不需要手动编写代码。
4. 浏览器启动参数方案(不推荐,全局生效)
基于Chromium内核的浏览器可以通过添加启动参数--disable-background-timer-throttling --disable-renderer-backgrounding,关闭浏览器对后台标签页的定时器节流、渲染进程后台挂起逻辑,让后台标签页保持和前台一致的运行状态。但该方案是全局生效,会导致所有后台标签页都保持高优先级运行,额外占用大量内存和CPU资源,日常使用不推荐。
补充说明:单纯在失焦后手动反复触发focus事件的方式,对大部分做了多重检测的网站无效,必须同时覆写状态属性、拦截失焦事件才能完全绕过检测。
内容的提问来源于stack exchange,提问作者flen

