With great power comes great responsibility.

Spider-Man

Here’s a script to disable the Page Visibility API and similar “tractics” used to track whether you’re browser tab is in the background or foreground. These methods are rarely used with responsibility and grace and most times are used to pause your video/audio the second the tab loses focus or the window goes to the background.

Use this with a browser add-on like Greasemonkey or Tampermonkey.

Note: As of this writing Greasemonkey version 4 has a terrible bug that makes it completely and utterly useless for this script. In Greasemonkey version 4 it’s only executing this user script within the top level parent window and it’s not executing it for each iframe on the web page. This is problematic when the video or audio is playing from an embedded iframe.

// ==UserScript==
// @name         Disable page visibility API
// @namespace    elitereloaded.io
// @version      0.1
// @description  Disable Page Visibility API and alternative workarounds.
// @author       Jason Harris <https://www.elitereloaded.io>
// @match        *://*/*
// @icon         data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
// @grant        none
// ==/UserScript==


(function(document) {
    'use strict';

    var timeoutId = null;

    function main()
    {
        var s = document.createElement('script');
        s.setAttribute('type', 'text/javascript');
        s.text = `
      setTimeout(function() {
        Object.defineProperty(document, 'visibilityState', {
            configurable: false,
            writable: false,
            value: 'visible',
        });

        Object.defineProperty(document, 'hidden', {
            configurable: false,
            writable: false,
            value: false,
        });

        window.addEventListener('visibilitychange', function handle(e) {
            console.log('Attempting to block window.visibiliychange event');
            e.stopImmediatePropagation();
            e.preventDefault();
        }, true);

        document.addEventListener('visibilitychange', function handle(e) {
            console.log('Attempting to block document.visibiliychange event');
            e.stopImmediatePropagation();
            e.preventDefault();
        }, true);

        window.addEventListener('webkitvisibilitychange', function handle(e) {
            console.log('Attempting to block window.webkitvisibilitychange event');
            e.stopImmediatePropagation();
            e.preventDefault();
        }, true);

        document.addEventListener('webkitvisibilitychange', function handle(e) {
            console.log('Attempting to block document.webkitvisibilitychange event');
            e.stopImmediatePropagation();
            e.preventDefault();
        }, true);

        window.addEventListener('mozvisibilitychange', function handle(e) {
            console.log('Attempting to block window.mozvisibilitychange event');
            e.stopImmediatePropagation();
            e.preventDefault();
        }, true);

        document.addEventListener('mozvisibilitychange', function handle(e) {
            console.log('Attempting to block document.mozvisibilitychange event');
            e.stopImmediatePropagation();
            e.preventDefault();
        }, true);

        document.addEventListener('blur', function handle(e) {
            console.log('Attempting to block document.blur event');
            e.stopImmediatePropagation();
            e.preventDefault();
        }, true);

        window.addEventListener('blur', function handle(e) {
            console.log('Attempting to block window.blur event');
            e.stopImmediatePropagation();
            e.preventDefault();
        }, true);

        window.addEventListener('pagehide', function handle(e) {
            console.log('Attempting to block window.pagehide event');
            e.stopImmediatePropagation();
            e.preventDefault();
        }, true);

				window.document.hasFocus = function() { return true; };
      }, 100);
		`;

      document.head.appendChild(s);

      if (timeoutId !== null) {
          console.log('clearing timeoutId');
          clearTimeout(timeoutId);
          timeoutId = null;
      }
  }

  timeoutId = setTimeout(main);

})(document);