當選項卡不活動時,保持 JS/jQuery 代碼在 Safari 中工作 (Keep the JS/jQuery code working in Safari when the tab is not active)


問題描述

當選項卡不活動時,保持 JS/jQuery 代碼在 Safari 中工作 (Keep the JS/jQuery code working in Safari when the tab is not active)

我有一個如下所示的 JS/jQuery 代碼,其中我希望在會話選項卡不活動時保持 JS/jQuery 代碼正常工作。

以下代碼在 Google Chrome 中非常好但它在 Safari 中不起作用。

jQuery(document).ready(function ($) {

    let lastActivity = <?php echo time(); ?>;  // Line A

    let now = <?php echo time(); ?>;

    let logoutAfter = 3600;   // page will logout after 1800 seconds if there is no activity

    let userName = "<?php echo $_SESSION['user_name']; ?>";

    let timer = setInterval(function () {
        now++;
        let delta = now ‑ lastActivity;
        console.log(delta);  // Line A
        if (delta > logoutAfter) {
            clearInterval(timer);
            //DO AJAX REQUEST TO close.php
            $.ajax({
                url: "/control/admin.php",
                type: 'GET', // GET also fine
                data: {action: 'logout', user_name: userName},
                success: function (data) {
                    window.location.href = "admin.php";
                },
                error: function (jqXHR, textStatus, errorThrown) {
                    alert(textStatus);
                }
            });
        }
    }, 1000); //<‑‑ you can increase it( till <= logoutAfter ) for better performance as suggested by @"Space Coding"
});

當標籤頁處於非活動狀態時,Line A 處的值不會在 Safari 中遞增,但在 Google Chrome 中可以正常工作. 在 Google Chrome 中,它按預期工作。


參考解法

方法 1:

You can replace counter (it counts seconds) with calculating time difference.

let lastActivity = new Date();
let logoutAfter = 3600;
...
let delta = (new Date()).getTime() ‑ lastActivity.getTime();
if (delta > logoutAfter) {
    ...
}

P.S. So it must work even if the script itself is frozen when tab is inactive. Interval handler will be called at the moment when user activate this tab.

方法 2:

This approach will not work properly with multiple tabs opened. If user open new tab and started working in it, the earlier tab will logout the user as he is not active in that tab.

To overcome this, I will suggest to check the last active time from server using ajax call instead of doing it with javascript only.

方法 3:

According to this very thorough (but old) answer, setInterval() execution on inactive tabs is limited to max 1/s, on both Safari and Chrome ‑ but not stopped. There are also plenty of questions here on SO about Javascript getting paused or de‑prioritised on inactive tabs, some of which include solutions:

  • How can I make setInterval also work when a tab is inactive in Chrome?
  • iOS 5 pauses JavaScript when tab is not active
  • Safari JavaScript setTimeout stops when minimized
  • Chrome: timeouts/interval suspended in background tabs?

  • </ul>

    Probably the best option to do what you are trying is to use Web workers:

    Web Workers are a simple means for web content to run scripts in background threads. The worker thread can perform tasks without interfering with the user interface.


    </blockquote>

    There is an example of how to do that in an answer to one of the questions above.

    But there is also a much simpler option, though you should evaluate if it is safe considering you are relying on this to log users out.

    My testing of your code reflects the question I linked to earlier which describes setInterval() being slowed, but not stopped. For me, Safari (v 13.1, macOS 10.14.6) does not actually fully pause Javascript, but slows down execution of the loop, by increasing amounts. I see this by opening the dev console, and watching the output of the console.log(delta) messages ‑ they slow right down, first running only every 2s, then 4s, and so on, though sometimes faster. But they do not stop.

    That output also gives a hint about the problem, and the solution. The delta values shown on the console do not represent the real time difference since lastActivity. They are just incrementing numbers. If you see a delta value appear on the console 10 seconds after the last one, it should logically be +10, right? But it is not, it is just one higher.

    And that's the problem here ‑ the code is not counting the true time difference, it is just counting iterations of the loop:

    let timer = setInterval(function () {
        now++;    // <‑‑ problem
    

    This code correctly sets now to the current time only if setInterval() runs exactly every second. But we know that when the tab is inactive, it does not. In that case it is just counting the number of times the loop runs, which has no relation to the real time elapsed.

    To solve this problem, we have to determine now based on the real time. To do that, let's switch to using JS to calculate our timestamps (PHP is rendered only once, on page load, so if you use it inside the loop it will just stay fixed at the initial value):

    // Note that JS gives us milliseconds, not seconds
    let lastActivity = Date.now();
    let now = Date.now();
    let logoutAfter = 3600 * 1000;
    
    let timer = setInterval(function () {
        // PHP won't work, time() is rendered only once, on page load 
        // let now = <?php echo time(); ?>;
        now = Date.now();
        let delta = now ‑ lastActivity;
        console.log('New timer loop, now:', now, '; delta:', delta);
    

    Now, even if there is a pause of 10s between iterations, delta will be the true measure of time elapsed since the page was loaded. So even if the user switches away to another tab, every time the loop runs, it will correctly track time, even if it doesn't happen every second.

    So what does this mean in your case?

    1. According to your report, JS is not running at all in the inactive tab. In that case, it can happen that the tab stays in the logged‑in state, long past the time the user should have been logged out. However, assuming JS starts up again when you switch back the tab, the very first iteration of the loop will correctly calculate the time elapsed. If it is greater than your logout period, you will be logged out. So even though the tab stayed logged in longer than it should have, the user can't use it, since as soon as they switch to it they will be logged out. Note that "as soon" actually means "within 1 second plus the time it takes for the AJAX query to successfully log the user out".

    2. In my testing, JS does not stop in an inactive Safari tab, but slows right down. In this case, it would mean that the user would be automatically logged out on the inactive tab, though not right at the time they should be. If the loop runs say every 8s, it could mean that the user would be logged out up to 7s later than they should have been. If iterations slow down even more, the delay can potentially be even more. Assuming JS starts up again as normal as soon as the user switches back the tab, behaviour will be exactly as above, the first iteration in that case will log them out.

    EDIT

    Here's simplified, complete code, and a JSFiddle showing it running and working.

    jQuery(document).ready(function($) {
        let lastActivity = Date.now();
        let now = Date.now();
        let logoutAfter = 3600 * 1000;
    
        let timer = setInterval(function() {    
            now = Date.now();
            let delta = now ‑ lastActivity;
            console.log('New timer loop, now:', now, '; delta:', delta);
    
            if (delta > logoutAfter) {
                alert('logout!');
            }
        }, 1000); 
    });
    

    (by flashvdemAkshay VanjareDon't Panic)

    參考文件

    1. Keep the JS/jQuery code working in Safari when the tab is not active (CC BY‑SA 2.5/3.0/4.0)

#safari #jquery #javascript #setInterval #PHP






相關問題

如何讓 <asp:menu> 在 Safari 中工作? (How can I get <asp:menu> working in Safari?)

jQuery 航點在 Safari 中不起作用 (jQuery waypoints not working in Safari)

iOS WebApp 不顯示啟動圖像 (iOS WebApp not showing startup image)

Jquery、Javascript 點擊菜單在 IE 和 Safari 上不起作用 (Jquery, Javascript click menu doesnt work on IE and Safari)

如果我沒有 Mac 和 Safari,我將如何重現 Javascript 錯誤? (How am I going to reproduce Javascript bugs if I don't have a Mac & Safari?)

Webkit 中的全局 Javascript 變量 (Global Javascript variables in Webkit)

Safari IndexOutOfRangeException (safari IndexOutOfRangeException)

Safari 2.0 中修改鍵的鍵盤事件 (Keyboard Events for Modifier Keys in Safari 2.0)

通過 HTML 在 Mobile Safari 上實現 UIPickerview (Implement UIPickerview on Mobile Safari via HTML)

Safari 的 jQuery 問題 (jQuery problem with Safari)

網頁上的音頻被截斷 (Audio cuts out on webpages)

當選項卡不活動時,保持 JS/jQuery 代碼在 Safari 中工作 (Keep the JS/jQuery code working in Safari when the tab is not active)







留言討論