pagehide and pageshow events don't work as expected on ios chrome

25,660

Solution 1

Below is the working code:

<script type="text/javascript">
        var heartbeat;
        var lastInterval;

        function clearTimers() {
            clearTimeout(heartbeat);
        }

        function getTime() {
            return (new Date()).getTime();
        }

        function intervalHeartbeat() {
            var now = getTime();
            var diff = now - lastInterval - 200;
            lastInterval = now;
            if(diff > 1000) { // don't trigger on small stutters less than 1000ms
                clearTimers();
            }
        }
        lastInterval = getTime();
        heartbeat = setInterval(intervalHeartbeat, 200);

You can find more details here: http://aawaara.com/post/74543339755/smallest-piece-of-code-thats-going-to-change-the-world

Solution 2

Pagehide and pageshow aren't the events you need for what you're trying to accomplish.

Instead, use the visibilitychange event in combination with document.hidden or document.visibilitystate, which should do exactly what you want.

This'll only work on supported browsers - which to date includes Chrome, but not Safari (yet). So to be safe, I'd call clearTimers() on visibilitychange, and fall back to calling it on pagehide only if document.visibilitystate is not defined.

See:

MDN: visibilitychange event

MDN: Using the Page Visibility API

Page Visibility - W3C recommendation, October 2013

Share:
25,660
amit_saxena
Author by

amit_saxena

Updated on September 02, 2020

Comments

  • amit_saxena
    amit_saxena over 3 years

    Apple documentation lists down the available iOS browser events here: https://developer.apple.com/library/archive/documentation/AppleApplications/Reference/SafariWebContent/HandlingEvents/HandlingEvents.html

    The 'pagehide' and 'pageshow' events seem to work fine on safari, but on chrome it only works on page load and unload. It doesn't work on:

    1. Pressing the home button, i.e. sending Chrome to background

    2. Switching tabs

    Below is a small Javascript snippet that you can use to verify it:

    <script type="text/javascript">
            window.addEventListener("pageshow", function(evt){
                alert('show');
            }, false);
            window.addEventListener("pagehide", function(evt){
                alert('hide');
            }, false);
    </script>
    

    What can I do to detect whether chrome was sent to background or not. I need to clear a setTimeout timer as soon as chrome is brought back to foreground. Any workarounds?

  • Doin
    Doin over 9 years
    The fact that this works at all (assuming I take your word that it in fact does), means that javascript timers are not being triggered on background tabs or while the browser is minimized. So you may as well call clearTimers() earlier, when the browser tab is sent to the background rather then when it returns to the foreground, if you can detect that. Also (I assume you realize?), on any desktop browser, the above code will NEVER call clearTimers(): These browsers continue running JS timers/events/code even in background tabs or when minimized.
  • amit_saxena
    amit_saxena over 9 years
    It works for me. And yes, I use that only on iOS devices by detecting that server side.
  • amit_saxena
    amit_saxena over 9 years
    Page visibility API has poor (not so good) browser support, and I had to spend effort trying to make it work across all browsers. You can find more details here: aawaara.com/post/88310470252/…
  • obiuquido144
    obiuquido144 over 8 years
    After upvoting this answer, I found out that the visibilitychange events suffer the same issue in iOS Chrome as the pageshow/pagehide events: NO EVENT IS FIRED on iOS Chrome on e.g. home key press, or deeplinking into an app/store. Amit's heartbeat solution that relies on timers clamping to 1000ms in backgrounded tabs seems to be a good workaround. I'd personally use a timeout over interval (intervals have the additional overhead of needing to be cleared vs. setTimeout callback function can have a condition that either clears the redirectTimer or sets a new timeout to call itself again).
  • obiuquido144
    obiuquido144 over 8 years
    For Chrome on iOS this 'heartbeat' approach seems to be the only working solution, as visibilitychange doesn't fire on iOS Chrome (similar to pageshow/pagehide not firing).
  • obiuquido144
    obiuquido144 over 8 years
    I take back my suggestion to use setTimeout instead of setInterval. During testing of my setTimeout implementation, on iOS Chrome, after coming back to the tab, my 100ms 'heartbeat' timeout would often fire only after the 2000ms timer that I wanted to cancel in the heartbeat callback (usually about 20-70ms too late). A setInterval implementation seems to consistently fire before the long timeout is triggered, which is what we want.