You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在移动端可靠检测React应用的页面focus/blur状态

iOS Safari 端可靠的页面焦点/浏览状态检测方案

问题根因

你之前用的window层blur/focus事件绑定方案在iOS Safari上失效是系统策略导致的:

  • 上滑关闭应用、切系统后台时,iOS会立即挂起页面JS执行线程,不会等待blur事件完成派发,自然无法触发回调
  • 从后台切回Safari时,在用户产生首次触摸/点击交互前,iOS会拦截所有自动触发的焦点类事件,防止页面未经用户许可偷跑逻辑,因此仅靠focus事件无法在无交互场景下触发状态更新
  • 桌面端没有这类JS挂起和交互拦截策略,所以原有方案可以正常运行

可靠实现方案

不要单一依赖focus/blur事件,采用多事件组合+优先级校验的方案,覆盖所有移动端场景:

  • 以Page Visibility API为核心判断依据,这是目前移动端兼容性最好、触发时机最稳定的页面可见状态标准,iOS 11+全量支持
  • 保留focus/blur事件做桌面端兼容补充
  • 绑定pageshow/pagehide事件,覆盖Safari标签切换、页面前进后退缓存场景
  • 兼容老版本iOS Safari的webkitvisibilitychange私有前缀事件
  • 增加首次交互兜底逻辑:切回页面后如果状态未同步,在用户第一次触摸屏幕时自动校准状态

React 可直接复用的实现代码

封装为自定义Hook,组件内直接调用即可拿到当前页面是否处于活跃浏览状态:

import { useEffect, useState } from 'react';

function usePageActiveStatus() {
  const [isActive, setIsActive] = useState(true);

  useEffect(() => {
    const syncStatus = () => {
      // 优先用可见性API判断,兜底用hasFocus
      const hidden = document.hidden ?? document.webkitHidden;
      if (typeof hidden === 'boolean') {
        setIsActive(!hidden);
        return;
      }
      setIsActive(document.hasFocus());
    };

    // 绑定所有需要监听的事件
    const listenEvents = [
      'visibilitychange',
      'webkitvisibilitychange',
      'focus',
      'blur',
      'pageshow',
      'pagehide'
    ];
    listenEvents.forEach(eventName => {
      window.addEventListener(eventName, syncStatus);
    });

    // iOS无交互切回兜底:首次触摸时校准状态,触发一次后自动解绑
    const calibrateOnFirstTouch = () => syncStatus();
    window.addEventListener('touchstart', calibrateOnFirstTouch, { once: true });

    // 组件挂载时先同步一次初始状态
    syncStatus();

    // 组件卸载时清理所有监听
    return () => {
      listenEvents.forEach(eventName => {
        window.removeEventListener(eventName, syncStatus);
      });
      window.removeEventListener('touchstart', calibrateOnFirstTouch);
    };
  }, []);

  return isActive;
}

// 组件内使用示例
function App() {
  const isPageActive = usePageActiveStatus();

  useEffect(() => {
    if (isPageActive) {
      console.log('用户正在浏览当前页面');
      // 恢复视频播放、消息轮询等逻辑
    } else {
      console.log('页面已切到后台/失焦');
      // 暂停播放、停止轮询等逻辑
    }
  }, [isPageActive]);

  return <div>页面内容</div>;
}

落地注意事项

  • 状态变化对应的回调逻辑不要写高耗时操作,iOS切后台时给页面预留的JS执行窗口极短,执行超时会被系统直接中断
  • 上滑杀应用的场景下,pagehide和visibilitychange事件会在JS线程挂起前优先触发,完全可以替代blur事件的检测能力
  • 如果需要兼容iOS 11以下的极老版本,可以额外绑定resize事件做兜底,老版本Safari切后台时会触发窗口尺寸变化,辅助校准状态

内容的提问来源于stack exchange,提问作者GLHF

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 09:54:31