pagehide and pageshow events don't work as expected on ios chrome
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: Using the Page Visibility API
Page Visibility - W3C recommendation, October 2013
amit_saxena
Updated on September 02, 2020Comments
-
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:
Pressing the home button, i.e. sending Chrome to background
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 over 9 yearsThe 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 callclearTimers()
: These browsers continue running JS timers/events/code even in background tabs or when minimized. -
amit_saxena over 9 yearsIt works for me. And yes, I use that only on iOS devices by detecting that server side.
-
amit_saxena over 9 yearsPage 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 over 8 yearsAfter upvoting this answer, I found out that the
visibilitychange
events suffer the same issue in iOS Chrome as thepageshow/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 over 8 yearsFor Chrome on iOS this 'heartbeat' approach seems to be the only working solution, as
visibilitychange
doesn't fire on iOS Chrome (similar topageshow/pagehide
not firing). -
obiuquido144 over 8 yearsI take back my suggestion to use
setTimeout
instead ofsetInterval
. 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). AsetInterval
implementation seems to consistently fire before the long timeout is triggered, which is what we want.