必须监听 visibilitychange 事件并立即执行处理函数,否则初始状态变化必然漏掉;单次读取 document.visibilityState 不可靠,因页面加载时状态已变或 iOS Safari 不触发事件。

直接读 document.visibilityState 或 document.hidden 做业务判断,90% 会出错;必须监听 visibilitychange 事件,并立即执行一次处理函数——否则初始状态变化必然漏掉。
为什么不能只读一次 document.visibilityState
页面加载完成时,document.visibilityState 可能已是 'hidden'(如 Chrome 预加载标签页、Firefox 休眠恢复),也可能卡在 'visible'(如 iOS Safari 切后台不触发事件)。单次判断无法反映用户真实行为变化。
- 在
DOMContentLoaded回调里检查状态,就认为“用户是否在看”,结果漏掉后续切换 - 把
document.hidden当作开关控制定时器启停,但该属性已被标记为“向后兼容”,现代浏览器推荐用visibilityState - 没处理 SSR 渲染时
document不存在导致的undefined报错
怎么正确绑定 visibilitychange 事件
这个事件只在 document 上触发,不冒泡,也不支持委托。绑定错对象或时机不对,等于没绑。
- 监听必须写成
document.addEventListener('visibilitychange', handler),不是window或某个 DOM 节点 - 脚本需在 DOM 加载完成后执行,推荐放在
DOMContentLoaded后,或使用defer属性加载script - 避免在
iframe子页面中监听父页面状态——子页面的document和父页面无关 - 注册监听后,立即调用一次处理函数,防止初始状态变化被漏掉
iOS Safari 的 visibilitychange 触发不可靠怎么办
iOS Safari 对事件触发极其保守:App 切后台、锁屏、甚至双击 Home 键唤出多任务界面,都可能不发事件。这不是 bug,是系统限制。
立即学习“前端免费学习笔记(深入)”;
-
document.visibilityState === 'visible'不等于页面在前台,它只反映浏览器渲染策略 - 需交叉验证:
document.hasFocus()辅助判断当前 tab 是否获得焦点(注意:iframe内调用返回的是子页面焦点) - 对关键逻辑(如答题倒计时、直播心跳),必须配合
pagehide事件兜底 - WebView 场景(如微信内嵌页)需确认是否启用 visibility 支持,部分老版本始终返回
'visible'
visibilitychange 触发后该停哪些 JS 行为
浏览器不会自动停掉 setInterval、requestAnimationFrame 或网络请求——这些都得你自己清。
-
setInterval/setTimeout轮询(如每 3s fetch 新消息)→ 必须clearInterval -
requestAnimationFrame动画循环 → 必须配对调用cancelAnimationFrame - 视频/音频播放 → 调用
video.pause(),但别直接video.currentTime = 0 - 非关键日志或埋点上报 → 攒在数组里,等回到
'visible'状态再批量发 - 暂停后恢复时,先检查是否已被手动清除,避免重复启动定时器
最常被漏掉的是轮播图、粒子动画、实时图表重绘这类视觉密集型操作。它们在后台跑着不卡界面,但内存和 GPU 消耗真实存在——只要页面有持续运行的 JS 逻辑,就值得加一层 visibilityState 判断。


















