¿Hay alguna manera de detectar si una ventana del navegador no está activa actualmente?

585

Tengo JavaScript que realiza actividad periódicamente. Cuando el usuario no está mirando el sitio (es decir, la ventana o pestaña no tiene el foco), sería bueno no ejecutarlo.

¿Hay alguna manera de hacer esto usando JavaScript?

Mi punto de referencia: Gmail Chat reproduce un sonido si la ventana que está utilizando no está activa.

Luke Francl
fuente
8
Para aquellos que no están satisfechos con las respuestas a continuación, consulte la requestAnimationFrameAPI o use la función moderna de que la frecuencia de setTimeout/ setIntervalse reduce cuando la ventana no es visible (1 segundo en Chrome, por ejemplo).
Rob W
2
document.body.onblur = function (e) {console.log ('lama');} funcionó para elementos no enfocados.
WhyMe
2
Ver esta respuesta para una solución compatible con multi-navegador que utiliza el W3C Página Visibilidad API, cayendo de nuevo a blur/ focusen los navegadores que no lo soportan.
Mathias Bynens
2
El 80% de las respuestas a continuación no son respuestas a esta pregunta . La pregunta se refiere a que actualmente no está activo, pero toneladas de respuestas a continuación son sobre no visibles, lo que no es una respuesta a esta pregunta.
Posiblemente deberían marcarse
La mayoría de las personas hablan de no activo cuando quieren decir no activo y no visible . Simplemente no está activo es fácil: solo maneja la ventana blur/ focuseventos ... eso será de uso limitado, ya que una ventana puede estar inactiva pero total o parcialmente visible (también hay iconos de "vista previa" en algunas barras de tareas que la gente espera seguir siendo actualizado).
rustyx

Respuestas:

691

Desde que escribió originalmente esta respuesta, una nueva especificación ha alcanzado el estado de recomendación gracias al W3C. La API de visibilidad de página (en MDN ) ahora nos permite detectar con mayor precisión cuándo una página está oculta para el usuario.

document.addEventListener("visibilitychange", onchange);

Soporte actual del navegador:

  • Chrome 13+
  • Internet Explorer 10+
  • Firefox 10+
  • Opera 12.10+ [ leer notas ]

El siguiente código recurre al método de desenfoque / enfoque menos confiable en navegadores incompatibles:

(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"});
})();

onfocusiny onfocusoutson necesarios para IE 9 y versiones anteriores, mientras que todos los demás utilizan onfocusy onblur, a excepción de iOS, que utiliza onpageshowy onpagehide.

Andy E
fuente
1
@bellpeace: IE debe propagarse focusiny focusoutdesde el iframe a la ventana superior. Para los navegadores más nuevos, solo tendría que manejar los eventos focusy bluren el windowobjeto de cada iframe . Debe usar el código actualizado que acabo de agregar, que al menos cubrirá esos casos en los navegadores más nuevos.
Andy E
3
@JulienKronegg: es por eso que mi respuesta menciona específicamente la API de Visibilidad de Página que entró en estado de borrador de trabajo después de que originalmente escribí mi respuesta. Los métodos de enfoque / desenfoque proporcionan una funcionalidad limitada para navegadores antiguos. La vinculación a otros eventos, como en su respuesta, no cubre mucho más que esto y está más en riesgo de diferencias de comportamiento (como IE no dispara el mouseout cuando aparece una ventana debajo del cursor). Sugeriría que una acción más apropiada sería mostrar un mensaje o icono que indique al usuario que las actualizaciones pueden ser menos frecuentes debido a la inactividad de la página.
Andy E
66
@AndyE Probé esta solución en cromo. Funciona si cambio las pestañas, pero no si cambio las ventanas (ALT + pestaña). ¿Deberia? Aquí hay un violín - jsfiddle.net/8a9N6/17
Tony Lâmpada
2
@Heliodor: Me gustaría mantener el código en la respuesta mínima por ahora. Nunca tuvo la intención de ser una solución completa de cortar y pegar, ya que los implementadores pueden querer evitar establecer una clase en el cuerpo y tomar una acción completamente diferente (como detener e iniciar un temporizador).
Andy E
8
@AndyE Parece que su solución solo funciona si el usuario cambia las pestañas o minimiza / maximiza la ventana. Sin embargo, el evento onchange no se activa si el usuario deja la pestaña activa, pero maximiza otro programa desde la barra de tareas. ¿Hay una solución para ese escenario? ¡Gracias!
user1491636
132

Usaría jQuery porque entonces todo lo que tienes que hacer es esto:

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

O al menos funcionó para mí.

Carson Wright
fuente
1
para mí esta llamada dos veces en iframe
msangel
En Firefox, si hace clic dentro de la consola de Firebug (en la misma página), el windowfoco se perderá, lo cual es correcto, pero dependiendo de cuál sea su intención, podría no ser lo que necesita.
Majid Fouladpour
21
Esto ya no funciona para las versiones actuales de los navegadores modernos, consulte la respuesta aprobada (API de visibilidad de página)
Jon z
Esta solución no funciona en iPad, utilice el evento "pagehow"
ElizaS
Tanto BLUR como FOCUS se disparan cuando se carga la página. Cuando abro una nueva ventana desde mi página, no sucede nada, pero una vez que la nueva ventana se cierra, ambos eventos se
activan
49

Hay 3 métodos típicos utilizados para determinar si el usuario puede ver la página HTML, sin embargo, ninguno de ellos funciona perfectamente:

  • Se supone que la API de visibilidad de página W3C debe hacer esto (compatible desde: Firefox 10, MSIE 10, Chrome 13). Sin embargo, esta API solo genera eventos cuando la pestaña del navegador se anula por completo (por ejemplo, cuando el usuario cambia de una pestaña a otra). La API no genera eventos cuando la visibilidad no se puede determinar con una precisión del 100% (por ejemplo, Alt + Tab para cambiar a otra aplicación).

  • El uso de métodos basados ​​en enfoque / desenfoque le da muchos falsos positivos. Por ejemplo, si el usuario muestra una ventana más pequeña en la parte superior de la ventana del navegador, la ventana del navegador perderá el foco ( onblurelevado) pero el usuario aún puede verlo (por lo que aún debe actualizarse). Ver también http://javascript.info/tutorial/focus

  • Confiar en la actividad del usuario (movimiento del mouse, clics, tecla tecleada) también le da muchos falsos positivos. Piense en el mismo caso que el anterior, o en un usuario que mira un video.

Para mejorar los comportamientos imperfectos descritos anteriormente, utilizo una combinación de los 3 métodos: API de visibilidad W3C, luego enfoque / desenfoque y métodos de actividad del usuario para reducir la tasa de falsos positivos. Esto permite gestionar los siguientes eventos:

  • Cambiar la pestaña del navegador a otra (100% de precisión, gracias a la API de Visibilidad de Página W3C)
  • Página potencialmente oculta por otra ventana, por ejemplo, debido a Alt + Tab (probabilística = no 100% precisa)
  • La atención del usuario no está centrada en la página HTML (probabilística = no es 100% precisa)

Así es como funciona: cuando el documento pierde el foco, la actividad del usuario (como el movimiento del mouse) en el documento se monitorea para determinar si la ventana es visible o no. La probabilidad de visibilidad de la página es inversamente proporcional al tiempo de la última actividad del usuario en la página: si el usuario no realiza actividad en el documento durante mucho tiempo, la página probablemente no sea visible. El siguiente código imita la API de visibilidad de página del W3C: se comporta de la misma manera pero tiene una pequeña tasa de falsos positivos. Tiene la ventaja de ser multi-navegador (probado en Firefox 5, Firefox 10, MSIE 9, MSIE 7, Safari 5, Chrome 9).

    <div id = "x"> </div>

    <script>
    / **
    Registra el controlador para el evento para el objeto dado.
    @param obj el objeto que generará el evento
    @param evType el tipo de evento: click, keypress, mouseover, ...
    @param fn la función de controlador de eventos
    @param isCapturing establece el modo de evento (verdadero = evento de captura, falso = evento burbujeante)
    @return true si el controlador de eventos se ha adjuntado correctamente
    * /
    función addEvent (obj, evType, fn, isCapturing) {
      if (isCapturing == null) isCapturing = false; 
      if (obj.addEventListener) {
        // Firefox
        obj.addEventListener (evType, fn, isCapturing);
        volver verdadero;
      } else if (obj.attachEvent) {
        // MSIE
        var r = obj.attachEvent ('on' + evType, fn);
        volver r;
      } más {
        falso retorno;
      }
    }

    // registrarse en el cambio potencial de visibilidad de la página
    addEvent (documento, "cambio potencial de visibilidad", función (evento) {
      document.getElementById ("x"). innerHTML + = "possibleVisilityChange: potencialHidden =" + document.potentialHidden + ", document.potentiallyHiddenSince =" + document.potentiallyHiddenSince + "s <br>";
    });

    // regístrate en la API de visibilidad de página del W3C
    var oculto = nulo;
    var visibilidadCambio = nulo;
    if (typeof document.mozHidden! == "undefined") {
      hidden = "mozHidden";
      visibilidadCambio = "mozvisibilitychange";
    } else if (typeof document.msHidden! == "undefined") {
      hidden = "msHidden";
      visibilidadCambio = "msvisibilitychange";
    } else if (typeof document.webkitHidden! == "undefined") {
      hidden = "webkitHidden";
      visibilidadCambio = "webkitvisibilitychange";
    } else if (typeof document.hidden! == "hidden") {
      hidden = "hidden";
      visibilidadCambio = "Cambio de visibilidad";
    }
    if (oculto! = nulo &&Cambio! = nulo) {
      addEvent (documento, visibilidadCambio, función (evento) {
        document.getElementById ("x"). innerHTML + = visibilidadCambio + ":" + hidden + "=" + document [hidden] + "<br>";
      });
    }


    var potencialPageVisibility = {
      pageVisibilityChangeThreshold: 3 * 3600, // en segundos
      init: function () {
        función setAsNotHidden () {
          var dispatchEventRequired = document.potentialHidden;
          document.potentialHidden = false;
          document.potentiallyHiddenSince = 0;
          if (dispatchEventRequired) dispatchPageVisibilityChangeEvent ();
        }

        function initPotentiallyHiddenDetection () {
          if (! hasFocusLocal) {
            // la ventana no tiene el foco => verifica la actividad del usuario en la ventana
            lastActionDate = new Date ();
            if (timeoutHandler! = null) {
              clearTimeout (timeoutHandler);
            }
            timeoutHandler = setTimeout (checkPageVisibility, potencialPageVisibility.pageVisibilityChangeThreshold * 1000 + 100); // +100 ms para evitar problemas de redondeo en Firefox
          }
        }

        función dispatchPageVisibilityChangeEvent () {
          unifiedVisilityChangeEventDispatchAllowed = false;
          var evt = document.createEvent ("Evento");
          evt.initEvent ("cambio potencial de visibilidad", verdadero, verdadero);
          document.dispatchEvent (evt);
        }

        función checkPageVisibility () {
          var potencialHiddenDuration = (hasFocusLocal || lastActionDate == null? 0: Math.floor ((new Date (). getTime () - lastActionDate.getTime ()) / 1000));
                                        document.potentiallyHiddenSince = potencialHiddenDuration;
          if (possibleHiddenDuration> = possiblePageVisibility.pageVisibilityChangeThreshold &&! document.potentialHidden) {
            // umbral de cambio de visibilidad de página incrementado => elevar el par
            document.potentialHidden = true;
            dispatchPageVisibilityChangeEvent ();
          }
        }

        var lastActionDate = nulo;
        var hasFocusLocal = true;
        var hasMouseOver = true;
        document.potentialHidden = false;
        document.potentiallyHiddenSince = 0;
        var timeoutHandler = nulo;

        addEvent (documento, "show de páginas", función (evento) {
          document.getElementById ("x"). innerHTML + = "pageshow / doc: <br>";
        });
        addEvent (documento, "ocultación de página", función (evento) {
          document.getElementById ("x"). innerHTML + = "pagehide / doc: <br>";
        });
        addEvent (ventana, "pagehow", función (evento) {
          document.getElementById ("x"). innerHTML + = "pageshow / win: <br>"; // aparece cuando la página muestra por primera vez
        });
        addEvent (ventana, "ocultación de página", función (evento) {
          document.getElementById ("x"). innerHTML + = "pagehide / win: <br>"; // no elevado
        });
        addEvent (documento, "mousemove", función (evento) {
          lastActionDate = new Date ();
        });
        addEvent (documento, "mouseover", función (evento) {
          hasMouseOver = verdadero;
          setAsNotHidden ();
        });
        addEvent (documento, "mouseout", función (evento) {
          hasMouseOver = falso;
          initPotentiallyHiddenDetection ();
        });
        addEvent (ventana, "desenfoque", función (evento) {
          hasFocusLocal = falso;
          initPotentiallyHiddenDetection ();
        });
        addEvent (ventana, "foco", función (evento) {
          hasFocusLocal = verdadero;
          setAsNotHidden ();
        });
        setAsNotHidden ();
      }
    }

    PotencialPageVisibility.pageVisibilityChangeThreshold = 4; // 4 segundos para probar
    PotencialPageVisibility.init ();
    </script>

Como actualmente no existe una solución de navegador cruzado que funcione sin falsos positivos, es mejor que lo piense dos veces antes de deshabilitar la actividad periódica en su sitio web.

Julien Kronegg
fuente
¿El uso de un operador de comparación estricto en la cadena 'undefined' en lugar de la palabra clave undefined causaría falsos positivos en el código anterior?
Jonathon
@kiran: En realidad, ESTÁ trabajando con Alt + Tab. No puede determinar si la página está oculta cuando hace una Alt + Tab porque puede cambiar a una ventana más pequeña, por lo que no puede garantizar que su página esté completamente oculta. Es por eso que uso la noción de "potencialmente oculto" (en el ejemplo, el umbral se establece en 4 segundos, por lo que debe cambiar a otra ventana usando Alt + Tab durante al menos 4 segundos). Sin embargo, su comentario muestra que la respuesta no fue tan clara, así que la reformulé.
Julien Kronegg
@JulienKronegg Creo que esta es la mejor solución hasta ahora. Sin embargo, el código anterior extremadamente necesita algunas refactorizaciones y abstracciones. ¿Por qué no lo subes a GitHub y dejas que la comunidad lo refactorice?
Jacob
1
@Jacob Estoy feliz de que te haya gustado mi solución. Siéntase libre de promocionarlo en un proyecto GitHub usted mismo. Doy el código con licencia Creative Commons BY creativecommons.org/licenses/by/4.0
Julien Kronegg
26

Hay una biblioteca ordenada disponible en GitHub:

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

Ejemplo:

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

He probado la versión 1.0.1 en todos los navegadores que tengo y puedo confirmar que funciona con:

  • IE9, IE10
  • FF 26.0
  • Chrome 34.0

... y probablemente todas las versiones más nuevas.

No funciona completamente con:

  • IE8: siempre indica que la pestaña / ventana está activa actualmente ( .now()siempre vuelve truepara mí)
Om nom nom
fuente
La respuesta aceptada causó problemas en IE9. Esta biblioteca funciona muy bien.
Tom Teman
20

Uso: API de visibilidad de página

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

Puedo usar ? http://caniuse.com/#feat=pagevisibility

l2aelba
fuente
La pregunta no es sobre la visibilidad de la página. Se trata de no activo / activo
gman
Creo que OP no está hablando de la función de ide
l2aelba
1
No estoy hablando de ide tampoco. Estoy hablando de alt-tabbing / cmd-tabbing para otra aplicación. De repente la página no está activa. La API de visibilidad de la página no me ayuda a saber si la página no está activa, solo me ayuda a saber si es posible que no esté visible.
gman
18

Creo un Chat Comet para mi aplicación, y cuando recibo un mensaje de otro usuario, uso:

if(new_message){
    if(!document.hasFocus()){
        audio.play();
        document.title="Have new messages";
    }
    else{
        audio.stop();
        document.title="Application Name";
    } 
}
infinito84
fuente
2
La solución más limpia con soporte para IE6
Paul Cooper
44
document.hasFocus()Es la forma más limpia de hacerlo. Todas las otras formas de usar la API de visibilidad o evento basado o buscar varios niveles de actividad del usuario / falta de actividad se vuelven demasiado complicadas y llenas de casos y agujeros. póngalo en un intervalo simple y genere un evento personalizado cuando cambien los resultados. Ejemplo: jsfiddle.net/59utucz6/1
danatcofo
1
Eficiente, y a diferencia de las otras soluciones, proporciona comentarios correctos cuando cambia a otra pestaña o ventana del navegador, e incluso a una aplicación diferente.
ow3n
No Doubt, es la forma más limpia, pero no funciona en
Firefox
1
Si abro las herramientas de Chrome Dev, document.hasFocus () es igual a falso. O incluso si hace clic en el panel superior del navegador, sucede lo mismo. No estoy seguro de que esta solución es adecuada para hacer una pausa de vídeo, animación, etc
tylik
16

Comencé a usar la respuesta wiki de la comunidad, pero me di cuenta de que no estaba detectando eventos de pestaña alternativa en Chrome. Esto se debe a que utiliza la primera fuente de eventos disponible y, en este caso, es la API de visibilidad de la página, que en Chrome parece no rastrear la tabulación alternativa.

Decidí modificar un poco el script para realizar un seguimiento de todos los eventos posibles para los cambios de foco de la página. Aquí hay una función que puede colocar:

function onVisibilityChange(callback) {
    var visible = true;

    if (!callback) {
        throw new Error('no callback given');
    }

    function focused() {
        if (!visible) {
            callback(visible = true);
        }
    }

    function unfocused() {
        if (visible) {
            callback(visible = false);
        }
    }

    // Standards:
    if ('hidden' in document) {
        document.addEventListener('visibilitychange',
            function() {(document.hidden ? unfocused : focused)()});
    }
    if ('mozHidden' in document) {
        document.addEventListener('mozvisibilitychange',
            function() {(document.mozHidden ? unfocused : focused)()});
    }
    if ('webkitHidden' in document) {
        document.addEventListener('webkitvisibilitychange',
            function() {(document.webkitHidden ? unfocused : focused)()});
    }
    if ('msHidden' in document) {
        document.addEventListener('msvisibilitychange',
            function() {(document.msHidden ? unfocused : focused)()});
    }
    // IE 9 and lower:
    if ('onfocusin' in document) {
        document.onfocusin = focused;
        document.onfocusout = unfocused;
    }
    // All others:
    window.onpageshow = window.onfocus = focused;
    window.onpagehide = window.onblur = unfocused;
};

Úselo así:

onVisibilityChange(function(visible) {
    console.log('the page is now', visible ? 'focused' : 'unfocused');
});

Esta versión escucha todos los diferentes eventos de visibilidad y activa una devolución de llamada si alguno de ellos causa un cambio. Los controladores focusedy se unfocusedaseguran de que la devolución de llamada no se llame varias veces si varias API detectan el mismo cambio de visibilidad.

Daniel Buckmaster
fuente
Chrome, por ejemplo, tiene ambos document.hiddeny document.webkitHidden. Sin el elseen la ifconstrucción tendríamos 2 llamadas de devolución de llamada ¿verdad?
Christiaan Westerbeek
@ChristiaanWesterbeek ¡Ese es un buen punto, no pensé en eso! Si puedes editar esta publicación, adelante y aceptaré :)
Daniel Buckmaster
Espera un minuto: la edición para agregar "más" sugerida por ChristiaanWesterbeek y agregada por @ 1.21Gigawatts no parece una buena idea: derrota la compra original de la idea de Daniel, que es probar todo métodos en paralelo. Y no hay riesgo de que la devolución de llamada se llame dos veces porque focus () y unfocused () suprimen las llamadas adicionales cuando nada cambia. Realmente parece que deberíamos volver a la primera rev.
Louis Semprini
@LouisSemprini es una gran captura. ¡Había olvidado la intención original del código! ¡He restaurado el original y he añadido una explicación!
Daniel Buckmaster
comprobando esto a partir de hoy, no detecta alt + tab al menos en Chrome 78 + macos
Hugo Gresse
7

Esto es realmente complicado. Parece que no hay solución dados los siguientes requisitos.

  • La página incluye iframes sobre los que no tiene control
  • Desea realizar un seguimiento del cambio de estado de visibilidad independientemente del cambio que se desencadena por un cambio de TAB (ctrl + tab) o un cambio de ventana (alt + tab)

Esto sucede porque:

  • La API de Visibilidad de la página puede informarle de manera confiable sobre un cambio de pestaña (incluso con iframes), pero no puede decirle cuándo el usuario cambia las ventanas.
  • Escuchar eventos de desenfoque / enfoque de ventana puede detectar alt + tabs y ctrl + tabs, siempre que el iframe no tenga foco.

Dadas estas restricciones, es posible implementar una solución que combine: la API de Visibilidad de la página - desenfoque / enfoque de la ventana - document.activeElement

Eso es capaz de:

  • 1) Ctrl + pestaña cuando la página principal tiene foco: SÍ
  • 2) Ctrl + pestaña cuando el iframe tiene foco: SI
  • 3) alt + tab cuando la página principal tiene foco: SÍ
  • 4) alt + tabulador cuando el iframe tiene foco: NO <- bummer

Cuando el iframe tiene foco, sus eventos de desenfoque / foco no se invocan en absoluto, y la API de Visibilidad de la página no se activará en alt + tab.

Construí sobre la solución de @ AndyE e implementé esta (casi buena) solución aquí: https://dl.dropboxusercontent.com/u/2683925/estante-components/visibility_test1.html (lo siento, tuve algunos problemas con JSFiddle).

Esto también está disponible en Github: https://github.com/qmagico/estante-components

Esto funciona en cromo / cromo. Funciona en Firefox, excepto que no carga el contenido del iframe (¿alguna idea de por qué?)

De todos modos, para resolver el último problema (4), la única forma de hacerlo es escuchar eventos de desenfoque / enfoque en el iframe. Si tiene algún control sobre los iframes, puede usar la API postMessage para hacerlo.

https://dl.dropboxusercontent.com/u/2683925/estante-components/visibility_test2.html

Todavía no he probado esto con suficientes navegadores. Si puede encontrar más información sobre dónde esto no funciona, hágamelo saber en los comentarios a continuación.

Tony Lâmpada
fuente
En mis pruebas también funcionó en IE9, IE10 y Chrome en Android.
Tony Lâmpada
1
Parece que el iPad necesita una solución completamente diferente - stackoverflow.com/questions/4940657/…
Tony Lâmpada
3
Todos estos enlaces son 404 :(
Daniel Buckmaster
6
var visibilityChange = (function (window) {
    var inView = false;
    return function (fn) {
        window.onfocus = window.onblur = window.onpageshow = window.onpagehide = function (e) {
            if ({focus:1, pageshow:1}[e.type]) {
                if (inView) return;
                fn("visible");
                inView = true;
            } else if (inView) {
                fn("hidden");
                inView = false;
            }
        };
    };
}(this));

visibilityChange(function (state) {
    console.log(state);
});

http://jsfiddle.net/ARTsinn/JTxQY/

yckart
fuente
5

esto funciona para mí en Chrome 67, Firefox 67,

if(!document.hasFocus()) {
    // do stuff
}
Samad
fuente
3

puedes usar:

(function () {

    var requiredResolution = 10; // ms
    var checkInterval = 1000; // ms
    var tolerance = 20; // percent


    var counter = 0;
    var expected = checkInterval / requiredResolution;
    //console.log('expected:', expected);

    window.setInterval(function () {
        counter++;
    }, requiredResolution);

    window.setInterval(function () {
        var deviation = 100 * Math.abs(1 - counter / expected);
        // console.log('is:', counter, '(off by', deviation , '%)');
        if (deviation > tolerance) {
            console.warn('Timer resolution not sufficient!');
        }
        counter = 0;
    }, checkInterval);

})();
maryam
fuente
3

En HTML 5 también puedes usar:

  • onpageshow: Secuencia de comandos que se ejecutará cuando la ventana se vuelva visible
  • onpagehide: Secuencia de comandos que se ejecutará cuando la ventana esté oculta

Ver:

Roberkules
fuente
2
Creo que esto está relacionado con el BFCache: cuando el usuario hace clic en Atrás o Adelante, no está relacionado con la página que se encuentra en la parte superior del escritorio de la computadora.
nonopolaridad
2

Esta es una adaptación de la respuesta de Andy E.

Esto hará una tarea, por ejemplo, actualizar la página cada 30 segundos, pero solo si la página está visible y enfocada.

Si no se puede detectar la visibilidad, solo se utilizará el foco.

Si el usuario enfoca la página, se actualizará inmediatamente

La página no se actualizará nuevamente hasta 30 segundos después de cualquier llamada ajax

var windowFocused = true;
var timeOut2 = null;

$(function(){
  $.ajaxSetup ({
    cache: false
  });
  $("#content").ajaxComplete(function(event,request, settings){
       set_refresh_page(); // ajax call has just been made, so page doesn't need updating again for 30 seconds
   });
  // check visibility and focus of window, so as not to keep updating unnecessarily
  (function() {
      var hidden, change, vis = {
              hidden: "visibilitychange",
              mozHidden: "mozvisibilitychange",
              webkitHidden: "webkitvisibilitychange",
              msHidden: "msvisibilitychange",
              oHidden: "ovisibilitychange" /* not currently supported */
          };
      for (hidden in vis) {
          if (vis.hasOwnProperty(hidden) && hidden in document) {
              change = vis[hidden];
              break;
          }
      }
      document.body.className="visible";
      if (change){     // this will check the tab visibility instead of window focus
          document.addEventListener(change, onchange,false);
      }

      if(navigator.appName == "Microsoft Internet Explorer")
         window.onfocus = document.onfocusin = document.onfocusout = onchangeFocus
      else
         window.onfocus = window.onblur = onchangeFocus;

      function onchangeFocus(evt){
        evt = evt || window.event;
        if (evt.type == "focus" || evt.type == "focusin"){
          windowFocused=true; 
        }
        else if (evt.type == "blur" || evt.type == "focusout"){
          windowFocused=false;
        }
        if (evt.type == "focus"){
          update_page();  // only update using window.onfocus, because document.onfocusin can trigger on every click
        }

      }

      function onchange () {
        document.body.className = this[hidden] ? "hidden" : "visible";
        update_page();
      }

      function update_page(){
        if(windowFocused&&(document.body.className=="visible")){
          set_refresh_page(1000);
        }
      }


  })();
  set_refresh_page();
})

function get_date_time_string(){
  var d = new Date();
  var dT = [];
  dT.push(d.getDate());
  dT.push(d.getMonth())
  dT.push(d.getFullYear());
  dT.push(d.getHours());
  dT.push(d.getMinutes());
  dT.push(d.getSeconds());
  dT.push(d.getMilliseconds());
  return dT.join('_');
}

function do_refresh_page(){

// do tasks here

// e.g. some ajax call to update part of the page.

// (date time parameter will probably force the server not to cache)

//      $.ajax({
//        type: "POST",
//        url: "someUrl.php",
//        data: "t=" + get_date_time_string()+"&task=update",
//        success: function(html){
//          $('#content').html(html);
//        }
//      });

}

function set_refresh_page(interval){
  interval = typeof interval !== 'undefined' ? interval : 30000; // default time = 30 seconds
  if(timeOut2 != null) clearTimeout(timeOut2);
  timeOut2 = setTimeout(function(){
    if((document.body.className=="visible")&&windowFocused){
      do_refresh_page();
    }
    set_refresh_page();
  }, interval);
}
Roger
fuente
Confiar en los métodos de enfoque / desenfoque no funciona (le da muchos falsos positivos), consulte stackoverflow.com/a/9502074/698168
Julien Kronegg
2

Para una solución sin jQuery, visite Visibility.js que proporciona información sobre tres estados de página

visible    ... page is visible
hidden     ... page is not visible
prerender  ... page is being prerendered by the browser

y también envoltorios de conveniencia para setInterval

/* Perform action every second if visible */
Visibility.every(1000, function () {
    action();
});

/* Perform action every second if visible, every 60 sec if not visible */
Visibility.every(1000, 60*1000, function () {
    action();
});

También está disponible un respaldo para navegadores antiguos (IE <10; iOS <7)

Niko
fuente
¿Qué pasa con el soporte del navegador? por ahora se bifurca bien en Chrome, Safari y Firefox.
Selva Ganapathi
1

Una forma un poco más complicada sería usar setInterval()para verificar la posición del mouse y comparar con la última verificación. Si el mouse no se ha movido en un tiempo determinado, el usuario probablemente esté inactivo.

Esto tiene la ventaja adicional de saber si el usuario está inactivo, en lugar de simplemente verificar si la ventana no está activa.

Como muchas personas han señalado, esta no siempre es una buena manera de verificar si el usuario o la ventana del navegador están inactivos, ya que el usuario puede no estar usando el mouse o viendo un video o similar. Solo estoy sugiriendo una posible forma de verificar la inactividad.

Austin Hyde
fuente
30
A menos que el usuario no tenga un mouse.
user1686
@ Annan: Es codinghorror.com/blog/2007/03/… ahora.
chiborg
Esto tampoco juega a los dados si el usuario está viendo un video
jamiew
puede usar onkeypress u otros eventos similares para restablecer el temporizador y resolver el problema que no es el mouse. Por supuesto que todavía no funcionaría para usuarios que buscan activamente a la página para ver un video, el estudio de una imagen, etc.
joshuahedlund
1

Para angular.js, aquí hay una directiva (basada en la respuesta aceptada) que permitirá que su controlador reaccione ante un cambio de visibilidad:

myApp.directive('reactOnWindowFocus', function($parse) {
    return {
        restrict: "A",
        link: function(scope, element, attrs) {
            var hidden = "hidden";
            var currentlyVisible = true;
            var functionOrExpression = $parse(attrs.reactOnWindowFocus);

          // 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);
          else if ("onfocusin" in document) {
                // IE 9 and lower:
            document.onfocusin = onshow;
                document.onfocusout = onhide;
          } else {
                // All others:
            window.onpageshow = window.onfocus = onshow;
                window.onpagehide = window.onblur = onhide;
            }

          function onchange (evt) {
                //occurs both on leaving and on returning
                currentlyVisible = !currentlyVisible;
                doSomethingIfAppropriate();
          }

            function onshow(evt) {
                //for older browsers
                currentlyVisible = true;
                doSomethingIfAppropriate();
            }

            function onhide(evt) {
                //for older browsers
                currentlyVisible = false;
                doSomethingIfAppropriate();
            }

            function doSomethingIfAppropriate() {
                if (currentlyVisible) {
                    //trigger angular digest cycle in this scope
                    scope.$apply(function() {
                        functionOrExpression(scope);
                    });
                }
            }
        }
    };

});

Puede usarlo como este ejemplo:, <div react-on-window-focus="refresh()">donde refresh()es una función de alcance en el alcance de cualquier controlador que esté en el alcance.

Steve Campbell
fuente
0

Aquí hay una solución sólida y moderna. (Corto un dulce 👌🏽)

document.addEventListener("visibilitychange", () => {
  console.log( document.hasFocus() )
})

Esto configurará un oyente para que se active cuando se active cualquier evento de visibilidad que podría ser un foco o desenfoque.

Cory Robinson
fuente
0

Si desea actuar en todo el desenfoque del navegador : como comenté, si el navegador pierde el foco, ninguno de los eventos sugeridos se activa. Mi idea es contar en un bucle y restablecer el contador si se dispara un evento. Si el contador alcanza un límite, hago un location.href a otra página. Esto también se dispara si trabajas en herramientas de desarrollo.

var iput=document.getElementById("hiddenInput");
   ,count=1
   ;
function check(){
         count++;
         if(count%2===0){
           iput.focus();
         }
         else{
           iput.blur();
         }
         iput.value=count;  
         if(count>3){
           location.href="http://Nirwana.com";
         }              
         setTimeout(function(){check()},1000);
}   
iput.onblur=function(){count=1}
iput.onfocus=function(){count=1}
check();

Este es un borrador probado con éxito en FF.

BF
fuente