HTML5怎样用visibilityChange获取页面显隐数据_HTML5显隐取数法【辑录】

visibilitychange 事件必须绑定到

document 而非 window,且不冒泡;判断显隐需用 document.hidden 而非 event.type;iOS 存在触发延迟或漏发问题;SPA 中需避免重复绑定和内存泄漏。

visibilitychange 事件必须监听 document,不是 window

很多人在写 visibilitychange 时习惯性绑定到 window,但这个事件只在 document 上触发。绑错对象会导致监听完全失效,且无任何报错提示。

  • document.addEventListener('visibilitychange', handler) ✅ 正确
  • window.addEventListener('visibilitychange', handler) ❌ 不会触发
  • 注意:该事件不冒泡,不能委托到父级元素

判断页面显隐要用 document.hidden,不是 event.type

事件名是 visibilitychange,但它的触发本身不带“当前是否可见”的状态信息。真正反映页面显隐状态的是 document.hidden 布尔值——true 表示页面被隐藏(如切到其他 tab、最小化浏览器),false 表示可见。

document.addEventListener('visibilitychange', () => {
  if (document.hidden) {
    console.log('页面已隐藏');
  } else {
    console.log('页面已显示');
  }
});

注意 Safari 和 iOS WebView 的兼容性陷阱

iOS 13+ Safari 和部分 WKWebView 环境中,document.hidden 在页面刚加载时可能为 false,但实际尚未渲染完成;更关键的是:当用户从后台切回 App 时,visibilitychange 有时延迟触发或漏发。

  • 不要依赖首次进入页面时的 document.hidden 值做初始化逻辑
  • 对关键行为(如暂停视频、停止心跳)建议加一层兜底:结合 pagehide/pageshow 事件
  • Android Chrome 和桌面端主流浏览器基本无此问题

避免重复绑定和内存泄漏

在单页应用(SPA)中,如果组件反复挂载/卸载(比如 React useEffect 或 Vue onMounted),容易多次绑定同一事件却未清理,导致 handler 被执行多次。

  • 每次绑定前先用 removeEventListener 清理旧监听(需保存 handler 引用)
  • 或使用一次性绑定 + 组件销毁时解绑的模式
  • 不要在内联函数里绑定:document.addEventListener('visibilitychange', () => {...}) → 无法解绑
const handleVisibilityChange = () => {
  // ...
};

// 绑定
document.addEventListener('visibilitychange', handleVisibilityChange);

// 解绑(例如在组件 unmount 时)
document.removeEventListener('visibilitychange', handleVisibilityChange);
页面显隐状态看似简单,但 document.hidden 的初始值、iOS 平台的事件时机、以及 SPA 中的监听生命周期,三者叠加最容易出隐蔽问题。