TL;DR version: A hacky way of detecting if a web page is running in a browser tab which isn't currently active. Possibly useful for not autoplaying video or audio. Currently only works in Firefox and Chrome. A rough demo is here.
(This post refers to subject matter covered in more detail in my previous blog post. You don't really need to read that to understand the core of what this post is about though.)
I'm probably very weird in the way I browse, but something that I experience on a more-than-daily basis is:
- Go to a site that has a bunch of items/stories/articles, many of which will have links. I'm thinking of things like a Twitter feed, Hacker News, a Gawker Media site, the B3TA newsletter, etc
- As I skim down the page, I r-click->open in new tab any items which look interesting and have links. I prefer to do this rather than to hop backwards and forwards between the original page and the linked pages.
- Unfortunately, some of those links might be to YouTube or other video services - and this might not be immediately obvious, especially with embedded videos or if URL shorteners are in use. You can then end up with one or more videos starting to autoplay, and you're forced to start going through the tabs to pause the videos until you're ready to watch them.
It struck me that it would be much more user-friendly if videos didn't autoplay if the page was in a window or tab that wasn't currently active or visible. Now, either my webdev and Google skills are deficient (quite possible), or this isn't as easy as you might expect.
Browsers do have focus and blur event triggers, but these don't really help - a page opened in a tab won't fire either of these until the tab is clicked on, and only Firefox fires a focus event when a page loads in an active tab/window.
As far as I can determine, there's no property of the Window object along the lines of 'focusState' that would make all of this dead easy to determine.
I then remembered reading a few posts about requestAnimationFrame, which has been introduced into the latest generation of browsers. The intended use-case for this functionality is to stop browsers burning CPU on animations/game cycles that a user can't actually see or interact with. This seemed like something which would help provide a solution to the problem I was thinking about.
Unforunately, things aren't straightforward. First off, only Firefox and Chrome currently have proper requestAnimationFrame support. Now, there are easy-to-use shims to keep things working on other browsers - unfortunately they do this by just doing the animations as normal on a page in an inactive tab, which doesn't help in my scenario.
Furthermore, the behaviour in the two browsers that do implement it differs - and from a bit of reading, I get the impression that they're not likely to converge to identical implementations. The differences are covered in the previous blog post - suffice to say we need to apply a bit of a fudge-factor to be able to support both browsers.
- Ensure the video is set to notautoplay
- On page load, initialize the following variables:
- animationCounter = 0
- stopAnimations = false
- windowState = undefined
- Set up some onFocus() and onBlur() handlers. The handlers will set windowState to "focussed" or "blurred" respectively.
- Use requestAnimationFrame an incrementCounter() function. This function - unsurprisingly - increments animationCounter, and if stopAnimations is not true, uses requestAnimationFrame to call itself again
- Use setTimeout() to call a determineState() function a second after page load
- When determineState() is called, set stopAnimations to true, clear the onFocus() and onBlur() handlers, and do one of the following:
- If windowState is "unknown"
- If windowState is "focussed", set the video to autoplay, as the user can definitely see it
- If windowState is "blurred", don't autoplay the video, as the user definitely can't see it. (I don't believe this state will ever happen, unless the user is juggling between tabs, but it makes sense to cover it for completeness.)
- If animationCounter is more than 3, the user probably has the page active, so autoplay the video
- If animationCounter is 2 or less, the user probably has the page inactive, so don't autoplay the video