Is there a way to detect if a browser window is not currently active?

355,669

Solution 1

Since originally writing this answer, a new specification has reached recommendation status thanks to the W3C. The Page Visibility API (on MDN) now allows us to more accurately detect when a page is hidden to the user.

document.addEventListener("visibilitychange", onchange);

Current browser support:

  • Chrome 13+
  • Internet Explorer 10+
  • Firefox 10+
  • Opera 12.10+ [read notes]

The following code falls back to the less reliable blur/focus method in incompatible browsers:

(function() {
  var hidden = "hidden";

  // Standards:
  if (hidden in document)
    document.addEventListener("visibilitychange", onchange);
  else if ((hidden = "mozHidden") in document)
    document.addEventListener("mozvisibilitychange", onchange);
  else if ((hidden = "webkitHidden") in document)
    document.addEventListener("webkitvisibilitychange", onchange);
  else if ((hidden = "msHidden") in document)
    document.addEventListener("msvisibilitychange", onchange);
  // IE 9 and lower:
  else if ("onfocusin" in document)
    document.onfocusin = document.onfocusout = onchange;
  // All others:
  else
    window.onpageshow = window.onpagehide
    = window.onfocus = window.onblur = onchange;

  function onchange (evt) {
    var v = "visible", h = "hidden",
        evtMap = {
          focus:v, focusin:v, pageshow:v, blur:h, focusout:h, pagehide:h
        };

    evt = evt || window.event;
    if (evt.type in evtMap)
      document.body.className = evtMap[evt.type];
    else
      document.body.className = this[hidden] ? "hidden" : "visible";
  }

  // set the initial state (but only if browser supports the Page Visibility API)
  if( document[hidden] !== undefined )
    onchange({type: document[hidden] ? "blur" : "focus"});
})();

onfocusin and onfocusout are required for IE 9 and lower, while all others make use of onfocus and onblur, except for iOS, which uses onpageshow and onpagehide.

Solution 2

I would use jQuery because then all you have to do is this:

$(window).blur(function(){
  //your code here
});
$(window).focus(function(){
  //your code
});

Or at least it worked for me.

Solution 3

There are 3 typical methods used to determine if the user can see the HTML page, however none of them work perfectly:

  • The W3C Page Visibility API is supposed to do this (supported since: Firefox 10, MSIE 10, Chrome 13). However, this API only raises events when the browser tab is fully overriden (e.g. when the user changes from one tab to another one). The API does not raise events when the visibility cannot be determined with 100% accuracy (e.g. Alt+Tab to switch to another application).

  • Using focus/blur based methods gives you a lot of false positive. For example, if the user displays a smaller window on top of the browser window, the browser window will lose the focus (onblur raised) but the user is still able to see it (so it still need to be refreshed). See also http://javascript.info/tutorial/focus

  • Relying on user activity (mouse move, clicks, key typed) gives you a lot of false positive too. Think about the same case as above, or a user watching a video.

In order to improve the imperfect behaviors described above, I use a combination of the 3 methods: W3C Visibility API, then focus/blur and user activity methods in order to reduce the false positive rate. This allows to manage the following events:

  • Changing browser tab to another one (100% accuracy, thanks to the W3C Page Visibility API)
  • Page potentially hidden by another window, e.g. due to Alt+Tab (probabilistic = not 100% accurate)
  • User attention potentially not focused on the HTML page (probabilistic = not 100% accurate)

This is how it works: when the document lose the focus, the user activity (such as mouse move) on the document is monitored in order to determine if the window is visible or not. The page visibility probability is inversely proportional to the time of the last user activity on the page: if the user makes no activity on the document for a long time, the page is most probably not visible. The code below mimics the W3C Page Visibility API: it behaves the same way but has a small false positive rate. It has the advantage to be multibrowser (tested on Firefox 5, Firefox 10, MSIE 9, MSIE 7, Safari 5, Chrome 9).


    <div id="x"></div>
     
    <script>
    /**
    Registers the handler to the event for the given object.
    @param obj the object which will raise the event
    @param evType the event type: click, keypress, mouseover, ...
    @param fn the event handler function
    @param isCapturing set the event mode (true = capturing event, false = bubbling event)
    @return true if the event handler has been attached correctly
    */
    function addEvent(obj, evType, fn, isCapturing){
      if (isCapturing==null) isCapturing=false; 
      if (obj.addEventListener){
        // Firefox
        obj.addEventListener(evType, fn, isCapturing);
        return true;
      } else if (obj.attachEvent){
        // MSIE
        var r = obj.attachEvent('on'+evType, fn);
        return r;
      } else {
        return false;
      }
    }
     
    // register to the potential page visibility change
    addEvent(document, "potentialvisilitychange", function(event) {
      document.getElementById("x").innerHTML+="potentialVisilityChange: potentialHidden="+document.potentialHidden+", document.potentiallyHiddenSince="+document.potentiallyHiddenSince+" s<br>";
    });
     
    // register to the W3C Page Visibility API
    var hidden=null;
    var visibilityChange=null;
    if (typeof document.mozHidden !== "undefined") {
      hidden="mozHidden";
      visibilityChange="mozvisibilitychange";
    } else if (typeof document.msHidden !== "undefined") {
      hidden="msHidden";
      visibilityChange="msvisibilitychange";
    } else if (typeof document.webkitHidden!=="undefined") {
      hidden="webkitHidden";
      visibilityChange="webkitvisibilitychange";
    } else if (typeof document.hidden !=="hidden") {
      hidden="hidden";
      visibilityChange="visibilitychange";
    }
    if (hidden!=null && visibilityChange!=null) {
      addEvent(document, visibilityChange, function(event) {
        document.getElementById("x").innerHTML+=visibilityChange+": "+hidden+"="+document[hidden]+"<br>";
      });
    }
     
     
    var potentialPageVisibility = {
      pageVisibilityChangeThreshold:3*3600, // in seconds
      init:function() {
        function setAsNotHidden() {
          var dispatchEventRequired=document.potentialHidden;
          document.potentialHidden=false;
          document.potentiallyHiddenSince=0;
          if (dispatchEventRequired) dispatchPageVisibilityChangeEvent();
        }
     
        function initPotentiallyHiddenDetection() {
          if (!hasFocusLocal) {
            // the window does not has the focus => check for  user activity in the window
            lastActionDate=new Date();
            if (timeoutHandler!=null) {
              clearTimeout(timeoutHandler);
            }
            timeoutHandler = setTimeout(checkPageVisibility, potentialPageVisibility.pageVisibilityChangeThreshold*1000+100); // +100 ms to avoid rounding issues under Firefox
          }
        }
     
        function dispatchPageVisibilityChangeEvent() {
          unifiedVisilityChangeEventDispatchAllowed=false;
          var evt = document.createEvent("Event");
          evt.initEvent("potentialvisilitychange", true, true);
          document.dispatchEvent(evt);
        }
     
        function checkPageVisibility() {
          var potentialHiddenDuration=(hasFocusLocal || lastActionDate==null?0:Math.floor((new Date().getTime()-lastActionDate.getTime())/1000));
                                        document.potentiallyHiddenSince=potentialHiddenDuration;
          if (potentialHiddenDuration>=potentialPageVisibility.pageVisibilityChangeThreshold && !document.potentialHidden) {
            // page visibility change threshold raiched => raise the even
            document.potentialHidden=true;
            dispatchPageVisibilityChangeEvent();
          }
        }
                            
        var lastActionDate=null;
        var hasFocusLocal=true;
        var hasMouseOver=true;
        document.potentialHidden=false;
        document.potentiallyHiddenSince=0;
        var timeoutHandler = null;
     
        addEvent(document, "pageshow", function(event) {
          document.getElementById("x").innerHTML+="pageshow/doc:<br>";
        });
        addEvent(document, "pagehide", function(event) {
          document.getElementById("x").innerHTML+="pagehide/doc:<br>";
        });
        addEvent(window, "pageshow", function(event) {
          document.getElementById("x").innerHTML+="pageshow/win:<br>"; // raised when the page first shows
        });
        addEvent(window, "pagehide", function(event) {
          document.getElementById("x").innerHTML+="pagehide/win:<br>"; // not raised
        });
        addEvent(document, "mousemove", function(event) {
          lastActionDate=new Date();
        });
        addEvent(document, "mouseover", function(event) {
          hasMouseOver=true;
          setAsNotHidden();
        });
        addEvent(document, "mouseout", function(event) {
          hasMouseOver=false;
          initPotentiallyHiddenDetection();
        });
        addEvent(window, "blur", function(event) {
          hasFocusLocal=false;
          initPotentiallyHiddenDetection();
        });
        addEvent(window, "focus", function(event) {
          hasFocusLocal=true;
          setAsNotHidden();
        });
        setAsNotHidden();
      }
    }
     
    potentialPageVisibility.pageVisibilityChangeThreshold=4; // 4 seconds for testing
    potentialPageVisibility.init();
    </script>

Since there is currently no working cross-browser solution without false positive, you should better think twice about disabling periodical activity on your web site.

Solution 4

Using : Page Visibility API

document.addEventListener( 'visibilitychange' , function() {
    if (document.hidden) {
        console.log('bye');
    } else {
        console.log('well back');
    }
}, false );

Can i use ? http://caniuse.com/#feat=pagevisibility

Solution 5

There is a neat library available on GitHub:

https://github.com/serkanyersen/ifvisible.js

Example:

// If page is visible right now
if( ifvisible.now() ){
  // Display pop-up
  openPopUp();
}

I've tested version 1.0.1 on all browsers I have and can confirm that it works with:

  • IE9, IE10
  • FF 26.0
  • Chrome 34.0

... and probably all newer versions.

Doesn't fully work with:

  • IE8 - always indicate that tab/window is currently active (.now() always returns true for me)
Share:
355,669
Luke Francl
Author by

Luke Francl

I am a software developer currently living in San Francisco. My speciality is web-based applications, lately using Ruby on Rails, though I have probably written more web applications in Tcl than most people. Check out my CV if you're looking for a software engineer.

Updated on July 08, 2022

Comments

  • Luke Francl
    Luke Francl almost 2 years

    I have JavaScript that is doing activity periodically. When the user is not looking at the site (i.e., the window or tab does not have focus), it'd be nice to not run.

    Is there a way to do this using JavaScript?

    My reference point: Gmail Chat plays a sound if the window you're using isn't active.

  • user1686
    user1686 almost 15 years
    Unless the user doesn't have a mouse.
  • Steve Hill
    Steve Hill over 13 years
    Perfect... I didn't ever think I needed to do this, until my boss asked me to implement it!
  • chiborg
    chiborg almost 13 years
  • Ali Shakiba
    Ali Shakiba almost 13 years
    Also consider stackoverflow.com/questions/831686/… to detect it for the first.
  • jamiew
    jamiew over 12 years
    This also doesn't play dice if the user is watching a video
  • joshuahedlund
    joshuahedlund over 12 years
    you could use onkeypress or other similar events to reset the timer and solve the non-mouse issue. Of course it still wouldn't work for users actively looking at the page to watch a video, study an image, etc.
  • bellpeace
    bellpeace almost 12 years
    How would you tell the difference between the case when user puts the tab in background and the case where she just switches to other (i)frame?
  • Andy E
    Andy E almost 12 years
    @bellpeace: IE should propagate focusin and focusout from the iframe to the upper window. For newer browsers, you'd just have to handle the focus and blur events on each iframe's window object. You should use the updated code I just added which will at least cover those cases in newer browsers.
  • Julien Kronegg
    Julien Kronegg over 11 years
    Relying on focus/blur methods do not work, see stackoverflow.com/a/9502074/698168
  • Julien Kronegg
    Julien Kronegg over 11 years
    Relying on focus/blur methods do not work (it gives you a lot of false positive), see stackoverflow.com/a/9502074/698168
  • Andy E
    Andy E over 11 years
    @JulienKronegg: that's why my answer specifically mentions the Page Visibility API which entered working draft status after I originally wrote my answer. The focus/blur methods provide limited functionality for older browsers. Binding to other events, as in your answer, doesn't cover a great deal more than this and is more at risk of behavioural differences (like IE not firing mouseout when a window pops up beneath the cursor). I would suggest a more appropriate action would be to display a message or icon indicating to the user that updates may be less frequent due to page inactivity.
  • Raynos
    Raynos over 11 years
    /*@cc_on!@*/false is stupid. Use document.focusin !== undefined
  • Andy E
    Andy E over 11 years
    @Raynos: you know where the edit button is... and you can do whatever you like with the function :-P
  • Raynos
    Raynos over 11 years
    @AndyE sign up to github and npm and open source all your functions! Do the world a favor.
  • namuol
    namuol over 11 years
    @Raynos How is document.focusin !== undefined a suitable replacement for /*@cc_on!@*/false? Besides the potential issues with minification, why is using @cc_on "stupid"?
  • Atario
    Atario about 11 years
    Great answer! Now — how do I, as a browser user, disable this ability? Making me keep an ad window active for a set period of time before you let me see the content is not cool.
  • Andy E
    Andy E about 11 years
    @Atario: I've never seen an implementation like that, but it's rather clever in a somewhat evil way. I don't think there's an easy way to disable the Page Visibility API in browsers. One way to combat this script in Firefox or Chrome would be to write a GreaseMonkey script that sets a hidden property on the document to false. This would force the script above to set the non-prefixed event handler which wouldn't fire in current versions of those browsers, but obviously it wouldn't work forever.
  • Jeff Jenkins
    Jeff Jenkins about 11 years
    I think this may be totally unrelated, to the original answer, but I found it very helpful with one minor alteration: for IE9, I needed to use ondomfocusin instead of onfocusin (and out, too).
  • msangel
    msangel almost 11 years
    for me this call twice in iframe
  • nonopolarity
    nonopolarity almost 11 years
    I think this is related to the BFCache: when the user clicks Back or Forward -- it is not related to the page being at the top of computer desktop.
  • Tony Lâmpada
    Tony Lâmpada over 10 years
    @AndyE I tried this solution on chromium. It works if I change tabs, but it doesn't if I change windows (ALT+tab). Should it? Here's a fiddle - jsfiddle.net/8a9N6/17
  • Andy E
    Andy E over 10 years
    @TonyLâmpada: Actually, the specification states that "On getting, the hidden attribute MUST return true if the Document contained by the top level browsing context (root window in the browser's viewport) [HTML5] is not visible at all. The attribute MUST return false if the Document contained by the top level browsing context is at least partially visible on at least one screen.". So I guess it is broken then... bug filed.
  • Tony Lâmpada
    Tony Lâmpada over 10 years
    Great bug description. Thanks for taking the time to do it!
  • Tony Lâmpada
    Tony Lâmpada over 10 years
    It seems this problems affects other browsers too (Firefox, at least). I used a combination of the VisibilityAPI + blur/focus to implement another solution (added an answer below)
  • Tony Lâmpada
    Tony Lâmpada over 10 years
    In my tests it also worked on IE9, IE10 and Chrome on Android.
  • Tony Lâmpada
    Tony Lâmpada over 10 years
    It seems IPAD needs a completely different solution - stackoverflow.com/questions/4940657/…
  • Andy E
    Andy E over 10 years
    @Tony: for me, Firefox does fire the event on minimise/maximise, but not for other conditions. I filed an issue on the Mozilla tracker shortly after filing the one for Chromium. Don't forget to vote on the bugs if you're registered ;-)
  • Majid Fouladpour
    Majid Fouladpour over 10 years
    In Firefox, if you click inside firebug console (on the same page), the window will loose focus, which is right, but depending on what your intention might not be what you need.
  • user3096443
    user3096443 over 10 years
    this will overwrite all current classes on the body element, how do you prevent that?
  • Andy E
    Andy E over 10 years
    @user3096443: use a regular expression to toggle the class instead (or you could add the class to document.documentElement instead.
  • Jon z
    Jon z about 10 years
    This no longer works for current versions of modern browsers, see the approved answer (Page Visibility API)
  • ElizaS
    ElizaS about 10 years
    This solution doesn't work on iPad please use "pageshow" event
  • WilliamK
    WilliamK about 10 years
    Where can we see an example of this script being used?
  • banny
    banny almost 10 years
    Today 12-06-14 google doodle also not work if we change the current tab, doodle stops an when we again focus on that tab then it runs.
  • Andy E
    Andy E almost 10 years
    @banny: I haven't checked, but it's likely that it's using requestAnimationFrame() instead of the Page Visibility API to achieve that.
  • salexch
    salexch almost 10 years
    please move the "var v = 'visible', h = 'hidden', evtMap = { focus:v, focusin:v, pageshow:v, blur:h, focusout:h, pagehide:h };" outside of the "onchange" function
  • Andy E
    Andy E almost 10 years
    @salexch: why? so it will operate some microseconds faster, because that's your personal preference, or some other pedantic reason? ;-)
  • salexch
    salexch almost 10 years
    a lot of people see this code, It will be nice if it will be clean as possible.. (my opinion).
  • HostedMetrics.com
    HostedMetrics.com over 9 years
    As an improvement, add and remove the class name from the 'body' element's classes, instead of setting the value and overwriting everything. Other code would like to use the class attribute of 'body' too.
  • Andy E
    Andy E over 9 years
    @Heliodor: I'd like to keep the code in the answer minimal for now. It was never intended to be a cut-and-paste complete solution, as implementors might want to avoid setting a class on the body and take a completely different action altogether (such as stopping and starting a timer).
  • SearchForKnowledge
    SearchForKnowledge over 9 years
    Both BLUR and FOCUS fires off when the page loads. When I open a new window from my page nothing happens but once the new window closes both event fires off :/ (using IE8)
  • user1491636
    user1491636 over 9 years
    @AndyE Your solution seems to only work if the user changes tabs, or minimizes/maximizes the window. However, the onchange event is not triggered if the user leaves the tab active, but maximizes another program over it from the taskbar. Is there a solution for that scenario? Thanks!
  • Andy E
    Andy E over 9 years
    @user149: sadly there isn't. I consider this to be a bug in the implementations though, so it might be a good idea to vote on them on the respective trackers (the links are in these comments).
  • Daniel Grima
    Daniel Grima over 9 years
    @AndyE I think this solution is very good and works well. I've been testing it out and I found a minor issue. I added the following code in the onchange function : if(document.body.className == "visible") $('p').append(document.body.className + ' || ' + new Date().toString() + '</br>'); All it does is output the class name and datetime in a paragraph when the tab is active again. Problem is that the text is being returned twice. Weird thing is that when I set breakpoints in Chrome Dev Tools, the breakpoint is only being fired once so I'm not sure what's the issue.
  • Daniel Grima
    Daniel Grima over 9 years
    @AndyE After further testing I've noticed that is only occurs when tabs are switched. If the window is minimised the code I've listed above is only run once.
  • Kahless
    Kahless over 9 years
    Why is this function in parentheses? Also how Would I go about using this. Do I just call it once and put whatever code I need to run within the onchange function? Sorry for my lack of knowledge.
  • Andy E
    Andy E over 9 years
    @JohnBob: the code is wrapped in an IIFE, and it's intended to be a starting point for your own implementation. That means you can either put your code in the onchange function, you can leave it as it is (if you want to keep the class change on the body), or you can call your own function at that point.
  • B.F.
    B.F. over 9 years
    If the whole browser loose focus most events don't fire. None of the events suggested here fits in this case.
  • Tom Teman
    Tom Teman over 9 years
    Accepted answer caused issues in IE9. This library works great.
  • Paul Cooper
    Paul Cooper about 9 years
    The cleanest solution with support back to IE6
  • Nimbosa
    Nimbosa over 8 years
    Good snippet. But when a window lose focus, its document.visibilityState value can still be visible and its document.hidden value can still be false in chrome, so blur !== hidden. So the evtMap in your snippet need more thoughts.
  • igorludi
    igorludi about 8 years
    Visibility and Focus are not the same thing! For instance, you can tile the windows and lose focus of the web page, while it is still visible. I believe that question's intent was FOCUS, though the author used a fuzzy "active" adjective. The answer above provides the visibility and is, in that respect, wrong (or better said off-topic).
  • Andy E
    Andy E about 8 years
    @igorludi: the question might have used the word "focus", but that's not what he was asking for. When you want to switch off or decrease the functionality of intermittently running scripts, you generally don't want to do this whilst the page is still visible. A window can go for a long time without focus whilst still being actively "looked at" by the user—if it is on another monitor or split across the screen, for instance. Yes, my answer falls back to focus but it's not elegant for this exact reason, but covers the majority of cases.
  • jcbvm
    jcbvm almost 8 years
    This is nice, but won't work for example when pressing window key + L to lock a windows pc.
  • Jonathon
    Jonathon almost 8 years
    Wouldn't using a strict comparison operator on the string 'undefined' instead of the undefined keyword cause false positives in the above code?
  • Daniel Buckmaster
    Daniel Buckmaster almost 8 years
    All these links are 404s :(
  • Julien Kronegg
    Julien Kronegg over 7 years
    @kiran: Actually it IS working with Alt+Tab. You cannot determine if the page is hidden when you do a Alt+Tab because you may switch to a smaller window so you can't guarantee that your page is fully hidden. This is why I use the notion of "potentially hidden" (in the example, the threshold is set to 4 seconds, so you need to switch to another window using Alt+Tab for at least 4 seconds). However your comment shows that the answer was not so clear, so I reworded it.
  • Johann
    Johann over 7 years
    This works but I discovered a condition where it does not work. On a Mac, if you have two browser windows open side by side with no overlapping of windows, the visibility will not change when you alternate bringing each window into focus. It seems to only work if one of the window that wants to detect visibility changes is partially obscured by some other window. You then need to then tap on the App icon on the Mac toolbar at the bottom of your desktop to switch between windows to get the visibility change event to fire.
  • danatcofo
    danatcofo about 7 years
    document.hasFocus() is the cleanest way to do it. All the other ways using the visibility api or event based or looking for various levels of user activity/lack of activity become overcomplicated and full of edge cases and holes. put it on a simple interval and raise a custom event when the results change. Example: jsfiddle.net/59utucz6/1
  • Jacob
    Jacob about 7 years
    @JulienKronegg I think this is the best solution yet. However, the code above extremely needs some refactoring and abstractions. Why don't you upload it to GitHub and let the community refactoring it?
  • Julien Kronegg
    Julien Kronegg about 7 years
    @Jacob I'm happy you liked my solution. Feel free to promote it into a GitHub project by yourself. I give the code with licence Creative Commons BY creativecommons.org/licenses/by/4.0
  • superphonic
    superphonic almost 7 years
    @AndroidDev That's because the window is still visible if you have them side by side. This checks for visibility not focus by default.
  • ow3n
    ow3n almost 7 years
    Efficient, and unlike the other solutions gives correct feedback when you switch to another browser tab or window, and even a different application.
  • Mohneesh Agrawal
    Mohneesh Agrawal over 6 years
    When you click on address bar in the same page this method is not working..
  • dansch
    dansch almost 6 years
    I'd prefer hiddenKey instead of hidden, since it does not describe whether or not anything is hidden, it is merely a string of how to access that property. Also, it would be nice to have a js comment about this, something like //could be window or document, depending on the fallback used. Thanks though!
  • Christiaan Westerbeek
    Christiaan Westerbeek over 5 years
    Chrome for example has both document.hidden and document.webkitHidden. Without the else in the if construction we would get 2 callback calls right?
  • Daniel Buckmaster
    Daniel Buckmaster over 5 years
    @ChristiaanWesterbeek That's a good point, I didn't think of that! If you can edit this post go ahead and I'll accept :)
  • gman
    gman over 5 years
    this doesn't work in Chrome68 on MacOS. I press Cmd-Tab to switch to another program and no event comes though. The question is is the window active, not if it's visible. Blur and Focus don't work either
  • gman
    gman over 5 years
    The question is not about page visibility. It's about not active / active
  • l2aelba
    l2aelba over 5 years
    I think OP is not talking about ide's function
  • gman
    gman over 5 years
    I'm not talking about ide's either. I'm talking about alt-tabbing/cmd-tabbing to another app. Suddenly the page is not active. The page visibility api does not help me know if the page is not active, it only helps me know if the is possibly not visible.
  • netizen
    netizen over 5 years
    Also when using two Chrome windows (at least, different profiles under Linux), when the page containing this code is behind, it still thinks it's visible (but it's not). The code from stackoverflow.com/a/15846434/909276 works in that scenario
  • hardik chugh
    hardik chugh almost 5 years
    No Doubt,its the cleanest way,but it doesn't work in firefox
  • Louis Semprini
    Louis Semprini almost 5 years
    Uh hey wait a minute: the edit to add "else"s suggested by ChristiaanWesterbeek and actually added by @1.21Gigawatts does not seem like a good idea: it defeats the original purchase of Daniel 's idea, which is to try all the supported methods in parallel. And there is no risk of the callback being called twice because focused() and unfocused() suppress extra calls when nothing is changing. Really seems like we should revert to the first rev.
  • Daniel Buckmaster
    Daniel Buckmaster almost 5 years
    @LouisSemprini that's a great catch. I had forgotten the original intent of the code! I've restored the original and added an explanation!
  • tylik
    tylik almost 5 years
    If I open Chrome Dev tools then document.hasFocus() equals to false. Or even if you click on the top panel of the browser, same happens. I'm not sure this solution is suitable to pause video, animation, etc
  • Hugo Gresse
    Hugo Gresse over 4 years
    checking this as of today, it does not detect alt+tab at least on Chrome 78 + macos
  • Cory Robinson
    Cory Robinson about 4 years
    @HugoGresse this snippet works perfectly fine on Chrome + MacOS.
  • Selva Ganapathi
    Selva Ganapathi about 4 years
    what about browser support ? for now forking good in chrome, safari, and firefox.
  • iair
    iair about 3 years
    Doesn't work with Alt-Tab (switching to another app).
  • Fatih
    Fatih almost 3 years
    Hello this is good answer but you should use the " window.onpageshow = window.onpagehide = window.onfocus = window.onblur = onchange;" block in any case. If not, this method not works if part/any corner of the page is visible.
  • Caleb Taylor
    Caleb Taylor over 2 years
    "For example, if the user displays a smaller window on top of the browser window, the browser window will lose the focus" Are you talking about iframes, if so that's true.
  • Julien Kronegg
    Julien Kronegg over 2 years
    @Caleb no, I'm talking about another application being in front of the web page (e.g. calculator). In this case, the web page looses the focus, but is still able to receive some events (e.g. mouse over events).
  • Luis Lobo
    Luis Lobo over 2 years
    Here the alt + tab worked... (Chrome 91)
  • Walter Stabosz
    Walter Stabosz over 2 years
    @tylik , the document won't have focus while you're in the dev tools. Paste this into the JS console, and then click on the document, the output should change from false to true. Code: setInterval(() => { console.log(document.hasFocus()) }, 1000);,
  • Karanveer Singh
    Karanveer Singh over 2 years
    a simple solution to check about visibilty is through "mouseleave" event listener on html tag. It will trigger even if user switches from 1 app to another. However, it will also trigger if user goes to browser's menu bar. Another issue is that this still does not work on Safari(is user switches apps), but other than that, its better than visibilitychange
  • AntonOfTheWoods
    AntonOfTheWoods about 2 years
    This library is completely abandoned. While it looks like it has a typescript version, it doesn't work in VSCode any more and even copy/pasting the source has lots of stuff that is no longer considered good practice for typescript