前端识别用户跳出跳回

70 阅读4分钟

在前端 PC 界面中,要识别用户从当前界面打开新开页(例如通过 target="_blank" 链接)后,又点击标签页回到原界面,主要依赖于浏览器提供的页面可见性(Page Visibility API)和窗口焦点(Window Focus/Blur)事件。

核心机制

  1. visibilitychange 事件和 document.visibilityState 属性:
    这是最推荐和最可靠的方式。当页面的可见性状态发生变化时(例如,用户切换到另一个标签页、最小化浏览器窗口、或者将浏览器窗口从一个屏幕移到另一个屏幕),visibilitychange 事件就会触发。document.visibilityState 属性会告诉你当前页面的可见状态:

    • visible: 页面内容至少部分可见。
    • hidden: 页面内容不可见(例如,标签页在后台、窗口被最小化、或者操作系统锁屏)。
  2. focusblur 事件 (在 window 对象上):
    这些事件在浏览器窗口(或标签页)获得或失去焦点时触发。

    • blur: 当窗口失去焦点时触发。
    • focus: 当窗口获得焦点时触发。

实现方法

结合这两个机制,我们可以更准确地判断用户是否从其他标签页切回当前页面。

示例代码:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>识别用户回访</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            margin: 20px;
        }
        .status-box {
            border: 1px solid #ccc;
            padding: 15px;
            margin-bottom: 20px;
            background-color: #f9f9f9;
        }
        .status-box p {
            margin: 5px 0;
        }
        .status-box .highlight {
            color: blue;
            font-weight: bold;
        }
    </style>
</head>
<body>

    <h1>页面回访检测示例</h1>
    <div class="status-box">
        <p>当前页面状态: <span id="visibilityStatus" class="highlight"></span></p>
        <p>窗口焦点状态: <span id="focusStatus" class="highlight"></span></p>
        <p>最后回访时间: <span id="lastReturnTime" class="highlight">从未</span></p>
        <p>回访计数: <span id="returnCount" class="highlight">0</span></p>
    </div>

    <p>
        <a href="https://www.google.com" target="_blank">点击这里打开一个新标签页 (Google)</a>
    </p>
    <p>
        <a href="https://www.baidu.com" target="_blank">点击这里打开一个新标签页 (Baidu)</a>
    </p>
    <p>
        <button id="simulateAction">模拟页面内操作</button>
    </p>

    <script>
        const visibilityStatusSpan = document.getElementById('visibilityStatus');
        const focusStatusSpan = document.getElementById('focusStatus');
        const lastReturnTimeSpan = document.getElementById('lastReturnTime');
        const returnCountSpan = document.getElementById('returnCount');

        let returnCount = 0;
        let lastVisibilityState = document.visibilityState;
        let lastFocusState = document.hasFocus(); // 初始焦点状态

        function updateStatus() {
            visibilityStatusSpan.textContent = document.visibilityState;
            focusStatusSpan.textContent = document.hasFocus() ? '有焦点' : '无焦点';
        }

        // 1. 监听 `visibilitychange` 事件
        document.addEventListener('visibilitychange', function() {
            const currentState = document.visibilityState;
            console.log(`Visibility changed from ${lastVisibilityState} to ${currentState}`);
            updateStatus();

            // 判断是否从隐藏状态变为可见状态
            if (lastVisibilityState === 'hidden' && currentState === 'visible') {
                console.log('用户从其他标签页/最小化状态回到了当前页面!');
                returnCount++;
                returnCountSpan.textContent = returnCount;
                lastReturnTimeSpan.textContent = new Date().toLocaleString();
                // 可以在这里执行你希望在用户回访时触发的逻辑
                alert('欢迎回来!');
            }
            lastVisibilityState = currentState;
        });

        // 2. 监听 `focus` 和 `blur` 事件 (作为辅助或替代方案)
        window.addEventListener('focus', function() {
            console.log('窗口获得焦点');
            updateStatus();
            // 注意:focus 事件在页面从最小化恢复时也会触发,
            // 但如果用户只是在同浏览器内切换标签页,visibilitychange 更准确。
            // 如果你只关心窗口级别的焦点,这个事件很有用。
        });

        window.addEventListener('blur', function() {
            console.log('窗口失去焦点');
            updateStatus();
        });

        // 初始状态更新
        updateStatus();

        // 模拟页面内操作,确保不是因为页面内点击导致的状态变化
        document.getElementById('simulateAction').addEventListener('click', function() {
            console.log('模拟页面内操作...');
            alert('你正在页面内操作!');
        });

        // 页面加载时检查初始状态
        window.onload = function() {
            console.log('页面加载完成。');
            updateStatus();
        };

    </script>
</body>
</html>

解释和注意事项:

  1. visibilitychange 是首选:

    • 当用户切换到另一个标签页时,当前页面的 visibilityState 会从 visible 变为 hidden
    • 当用户再切换回当前标签页时,visibilityState 会从 hidden 变回 visible
    • 这个事件在用户最小化浏览器窗口或从其他应用程序切换回来时也会触发。
    • 通过判断 lastVisibilityState === 'hidden' && currentState === 'visible',可以准确捕捉到用户从“不可见”状态回到“可见”状态的瞬间。
  2. focusblur 作为补充:

    • blur 事件在用户点击新标签页的链接时立即触发,因为当前窗口失去了焦点。
    • focus 事件在用户点击回当前标签页时触发,因为当前窗口重新获得了焦点。
    • 然而,focusblur 事件的粒度比 visibilitychange 更粗。例如,如果用户只是点击了浏览器外部的某个应用程序,blur 也会触发。如果用户在同一个浏览器窗口内切换标签页,focusblur 也会触发,但 visibilitychange 更能体现页面本身的可见性状态。
    • 通常,visibilitychange 已经足够满足“用户从其他标签页回来”的需求。
  3. 何时触发?

    • 当用户点击 target="_blank" 的链接打开新标签页时,原页面会立即触发 blur 事件,随后触发 visibilitychange 事件(hidden)。
    • 当用户从新标签页切换回原标签页时,原页面会触发 focus 事件,随后触发 visibilitychange 事件(visible)。
  4. 局限性:

    • 新标签页关闭: 如果用户打开新标签页后,直接关闭了新标签页而不是切换回原标签页,原标签页也会重新获得焦点和可见性。这通常也是你希望检测到的“回访”行为。
    • 浏览器回退/前进: 如果用户通过浏览器的回退/前进按钮导航,这不属于“打开新标签页后回来”的范畴,而是页面历史导航。这些事件不会触发。
    • 移动端: 在移动端,页面的生命周期和可见性处理可能更复杂,但 visibilitychange API 在移动浏览器中同样适用且是标准。

总结:

使用 document.addEventListener('visibilitychange', ...) 并检查 document.visibilityStatehiddenvisible 的变化,是识别用户从其他标签页(或最小化状态)回访当前页面的最佳和最兼容的方式。