多显示器下 `document.hidden` 判断失效,推送全挂了

深漂独立开发者 中级 1小时前 483 浏览 13 点赞 约 1 分钟

写 ShelfTalk 的推送系统时,我想着用户正盯着网页聊天,桌面再弹个一模一样的通知多烦人。于是加了个「优化」:

export const sendPushNotification = (title, options = {}, requireHidden = false) => {
  if (!('Notification' in window)) return;

  if (Notification.permission === 'granted') {
    if (requireHidden && !document.hidden) {
      return; // 以为用户在看,就不发了
    }
    // ...发送通知
  }
};

本地单显示器测全过,上线后用户反馈:完全收不到推送。

document.hidden === false 只代表「操作系统能看见窗口」,不代表「人在看」。

  • 双显示器:ShelfTalk 挂副屏,主屏干别的,document.hidden 依然 false,通知被吞
  • 公司电脑没关浏览器,回家打开笔记本,公司那台「可见」,家里这台也收不到
  • 平板支在桌上跑着网页,人早走了,OS 觉得可见,推送照样不发
多显示器下 `document.hidden` 判断失效,推送全挂了

MDN 文档写得明明白白:document.hidden 只管页面可见性状态,跟人类注意力没关系。我却把布尔值强行赋予了「用户正在注视」的含义。

修法:直接删掉那段「聪明」代码

- if (requireHidden && !document.hidden) {
-   return;
- }
+ // 通知照常发,哪怕标签页可见
+ // 保证多设备、多显示器场景都能触达

提交记录:b0c9b1645b4543e48778fea7137cdf5d4761b10e

三个教训

  • 别只在自己的开发机上测:单显示器根本复现不了,真实环境全是多屏、多设备、后台挂着
  • API 语义别脑补document.hidden 是给 OS 看的,不是给人看的
  • 宁可重复提醒,别静默丢消息:多响一次顶多烦人,漏掉一条才是把核心功能搞坏了

现在 ShelfTalk 可能在你盯着屏幕时也会响一下,但至少永远不会漏。🔔
javascriptgithubShelfTalkPage Visibility APIPush Notification

全部回复 (3)

调参侠小美 初级 1小时前
这坑我踩过两次了,第一次是用 Page Visibility API 判断用户离开,结果用户只是切到另一个标签页还盯着屏幕呢

后来改用 IntersectionObserver + mousemove/keydown 组合,再加个心跳包,才勉强算「真在看」

0 回复
小Ray在路上 中级 1小时前
多显示器下这块真坑,我改用 visibilitychange + focus/blur 事件兜底了
0 回复
老陈 专家 1小时前
我这边直接监听 window.onfocus/onblur 再配合 document.visibilityState 双重判断,稳多了
0 回复

发表回复

支持 Markdown 格式