Last Updated: March 02, 2018
·
12.9K
· hasenj

How to tell if app/page has focus

There's actually a page visibility API, documented on MDN.

However, it only works when the user switches tabs inside the browser window. If the browser window itself loses focus (while your tab is selected), the page visibility api won't tell you that you lost focus! This can be seen in this demo page for the visibility api. See what happens when switch tabs vs when you switch to another window.

On the other hand, we have a generic function, document.hasFocus that can tell us whether the tab is focused or not, at this time.

We also have "blur" and "focus" events attached to the window object, that get fired when the browser get in or out of focus.

The visibility API exposes another event, "visibilitychange", that gets fired when the current tab gets in our out of focus (within the context of the browser window).

We need to combine these two mechanisms. I will use knockout because it gives us observables that we can "subscribe" to when they change; which we will use to detect focus events.

var page_has_focus = ko.observable();

var checkFocus = function() {
    page_has_focus(document.hasFocus());
}

Next, we subscribe to all events that change focus:

document.addEventListener("visibilitychange", checkFocus);
window.addEventListener("focus", checkFocus);
window.addEventListener("blur", checkFocus);

But wait, there's a problem. Not all browsers expose the visibility change event as "visibilitychange". Look again at the MDN docs for it.

So, we make it cross-browser:

var visibilityChange;
if (typeof document.hidden !== "undefined") {
    visibilityChange = "visibilitychange";
} else if (typeof document.mozHidden !== "undefined") {
    visibilityChange = "mozvisibilitychange";
} else if (typeof document.msHidden !== "undefined") {
    visibilityChange = "msvisibilitychange";
} else if (typeof document.webkitHidden !== "undefined") {
    visibilityChange = "webkitvisibilitychange";
}

document.addEventListener(visibilityChange, checkFocus);
window.addEventListener("focus", checkFocus);
window.addEventListener("blur", checkFocus);

And to make the code cleaner, we wrap the whole thing into a IIFE (immediately invoked function), and to make it extra robust, we will return a read-only version of the observable, by wrapping it in a simple computed.

So, here's the result:

var page_has_focus = (function() {
    var page_has_focus = ko.observable();

    var checkFocus = function() {
        page_has_focus(document.hasFocus());
    }
    checkFocus(); // init

    var visibilityChange;
    if (typeof document.hidden !== "undefined") {
        visibilityChange = "visibilitychange";
    } else if (typeof document.mozHidden !== "undefined") {
        visibilityChange = "mozvisibilitychange";
    } else if (typeof document.msHidden !== "undefined") {
        visibilityChange = "msvisibilitychange";
    } else if (typeof document.webkitHidden !== "undefined") {
        visibilityChange = "webkitvisibilitychange";
    }

    document.addEventListener(visibilityChange, checkFocus);
    window.addEventListener("focus", checkFocus);
    window.addEventListener("blur", checkFocus);

    return ko.computed(function() {
        return page_has_focus();
    });
})();

Now let's test it!

Type the following into the console:

page_has_focus.subscribe(function(yes) {
    if(yes) {
        console.log("Page got focus");
    } else {
        console.log("Page lost focus");
    }
});

Now if you switch tabs and come back, you will see "Page lost focus" followed by a "Page got focus" message on the console.

If you switch to another application and come back, you will see the same.

You can also try to switch to another application such that the console is still visible; and you will still see the same result.

Note: the console itself counts as another tab; so after you type the code into the console, click inside the document to give it focus.

1 Response
Add your response

Why should we use even visibility change API? The window focus and blur event listeners works good. And even without knockout.js . Please take a look:

var checkFocus = function() {
console.log(document.hasFocus());
}
checkFocus(); // init

window.addEventListener("focus", checkFocus);
window.addEventListener("blur", checkFocus);

over 1 year ago ·