C'è un modo per rilevare se una finestra del browser non è attualmente attiva?


585

Ho JavaScript che sta svolgendo attività periodicamente. Quando l'utente non sta guardando il sito (ovvero, la finestra o la scheda non ha lo stato attivo), sarebbe bello non funzionare.

C'è un modo per farlo usando JavaScript?

Il mio punto di riferimento: Gmail Chat riproduce un suono se la finestra che stai utilizzando non è attiva.


8
Per coloro che non sono soddisfatti delle risposte di seguito, controlla l' requestAnimationFrameAPI o utilizza la moderna funzionalità che riduce la frequenza di setTimeout/ setIntervalquando la finestra non è visibile (1 secondo in Chrome, ad esempio).
Rob W,

2
document.body.onblur = function (e) {console.log ('lama');} ha funzionato per elementi non focalizzati.
WhyMe

2
Vedi questa risposta per una soluzione compatibile con più browser che utilizza l'API di visibilità della pagina del W3C, ricadendo in blur/ focusnei browser che non la supportano.
Mathias Bynens,

2
L'80% delle risposte di seguito non sono risposte a questa domanda . La domanda non è attualmente attiva, ma tonnellate di risposte qui sotto non sono visibili, il che non è una risposta a questa domanda. Probabilmente dovrebbero essere contrassegnati come "non una risposta"
gman

Molte persone parlano di non attivo quando significano non attivo e non visibile . Semplicemente non attivo è facile - basta gestire la finestra blur/ focuseventi ... che saranno di utilità limitata, poiché una finestra può essere inattiva ma completamente o parzialmente visibile (ci sono anche icone di "anteprima" in alcune barre delle applicazioni che le persone si aspettano di continuare a essere aggiornato).
Rustyx

Risposte:


691

Da quando ho scritto questa risposta, una nuova specifica ha raggiunto lo stato di raccomandazione grazie al W3C. L' API di visibilità della pagina (su MDN ) ora ci consente di rilevare con maggiore precisione quando una pagina è nascosta all'utente.

document.addEventListener("visibilitychange", onchange);

Supporto browser corrente:

  • Chrome 13+
  • Internet Explorer 10+
  • Firefox 10+
  • Opera 12.10+ [ leggi note ]

Il codice seguente ricade sul metodo di sfocatura / messa a fuoco meno affidabile nei browser incompatibili:

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

onfocusine onfocusoutsono richiesti per IE 9 e versioni precedenti, mentre tutti gli altri utilizzano onfocuse onblur, ad eccezione di iOS, che utilizza onpageshowe onpagehide.


1
@bellpeace: IE dovrebbe propagarsi focusine dall'iframe focusoutalla finestra superiore. Per i browser più recenti, dovresti solo gestire gli eventi focuse sull'oggetto di blurogni iframe window. Dovresti usare il codice aggiornato che ho appena aggiunto che coprirà almeno quei casi nei browser più recenti.
Andy E,

3
@JulienKronegg: ecco perché la mia risposta menziona specificamente l'API di visibilità della pagina che è entrata nello stato della bozza di lavoro dopo che avevo scritto la mia risposta. I metodi di messa a fuoco / sfocatura offrono funzionalità limitate per i browser meno recenti. Il legame con altri eventi, come nella tua risposta, non copre molto di più di questo ed è più a rischio di differenze comportamentali (come IE non attiva il mouseout quando si apre una finestra sotto il cursore). Suggerirei un'azione più appropriata sarebbe quella di visualizzare un messaggio o un'icona che indica all'utente che gli aggiornamenti potrebbero essere meno frequenti a causa dell'inattività della pagina.
Andy E

6
@AndyE Ho provato questa soluzione sul cromo. Funziona se cambio tab, ma non se cambio windows (ALT + tab). Dovrebbe? Ecco un violino - jsfiddle.net/8a9N6/17
Tony Lâmpada

2
@Heliodor: mi piacerebbe mantenere il codice nella risposta minima per ora. Non è mai stato pensato per essere una soluzione completa taglia e incolla, poiché gli implementatori potrebbero voler evitare di impostare una classe sul corpo e intraprendere un'azione completamente diversa (come l'arresto e l'avvio di un timer).
Andy E

8
@AndyE La tua soluzione sembra funzionare solo se l'utente cambia scheda o minimizza / massimizza la finestra. Tuttavia, l'evento onchange non viene attivato se l'utente lascia la scheda attiva, ma massimizza un altro programma su di esso dalla barra delle applicazioni. C'è una soluzione per quello scenario? Grazie!
user1491636

132

Vorrei usare jQuery perché poi tutto ciò che devi fare è questo:

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

O almeno ha funzionato per me.


1
per me questa chiamata due volte in iframe
msangel

In Firefox, se fai clic all'interno della console di firebug (sulla stessa pagina), la windowmessa a fuoco verrà persa, il che è giusto, ma a seconda di ciò che la tua intenzione potrebbe non essere quella di cui hai bisogno.
Majid Fouladpour,

21
Questo non funziona più per le versioni attuali dei browser moderni, vedere la risposta approvata (API di visibilità della pagina)
Jon z

Questa soluzione non funziona su iPad, si prega di utilizzare l'evento "pageshow"
ElizaS

BLUR e FOCUS vengono attivati ​​quando la pagina viene caricata. Quando apro una nuova finestra dalla mia pagina non succede nulla, ma una volta che la nuova finestra si chiude entrambi gli eventi si accendono: / (usando IE8)
SearchForKnowledge

49

Esistono 3 metodi tipici utilizzati per determinare se l'utente può visualizzare la pagina HTML, tuttavia nessuno di questi funziona perfettamente:

  • L' API W3C Page Visibility dovrebbe eseguire questa operazione (supportata da: Firefox 10, MSIE 10, Chrome 13). Tuttavia, questa API genera eventi solo quando la scheda del browser è completamente sostituita (ad es. Quando l'utente passa da una scheda all'altra). L'API non genera eventi quando la visibilità non può essere determinata con una precisione del 100% (ad esempio Alt + Tab per passare a un'altra applicazione).

  • L'uso di metodi basati sulla messa a fuoco / sfocatura offre molti falsi positivi. Ad esempio, se l'utente visualizza una finestra più piccola nella parte superiore della finestra del browser, la finestra del browser perderà il focus (in onblurrilievo) ma l'utente sarà ancora in grado di vederlo (quindi deve ancora essere aggiornato). Vedi anche http://javascript.info/tutorial/focus

  • Affidarsi all'attività dell'utente (spostamento del mouse, clic, digitazione dei tasti) ti dà anche molti falsi positivi. Pensa allo stesso caso sopra o a un utente che guarda un video.

Al fine di migliorare i comportamenti imperfetti sopra descritti, utilizzo una combinazione dei 3 metodi: W3C Visibility API, quindi focus / blur e metodi di attività dell'utente al fine di ridurre il tasso di falsi positivi. Ciò consente di gestire i seguenti eventi:

  • Modifica della scheda del browser in un'altra (precisione del 100%, grazie all'API di visibilità della pagina del W3C)
  • Pagina potenzialmente nascosta da un'altra finestra, ad esempio a causa di Alt + Tab (probabilistico = non preciso al 100%)
  • Attenzione dell'utente potenzialmente non focalizzata sulla pagina HTML (probabilistica = non accurata al 100%)

Funziona così: quando il documento perde lo stato attivo, l'attività dell'utente (come lo spostamento del mouse) sul documento viene monitorata per determinare se la finestra è visibile o meno. La probabilità di visibilità della pagina è inversamente proporzionale al tempo dell'ultima attività dell'utente sulla pagina: se l'utente non fa attività sul documento per un lungo periodo, molto probabilmente la pagina non è visibile. Il codice seguente imita l'API di visibilità della pagina del W3C: si comporta allo stesso modo ma ha una piccola percentuale di falsi positivi. Ha il vantaggio di essere multibrowser (testato su Firefox 5, Firefox 10, MSIE 9, MSIE 7, Safari 5, Chrome 9).

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

    <Script>
    / **
    Registra il gestore all'evento per l'oggetto specificato.
    @param obj l'oggetto che genererà l'evento
    @param ev Digitare il tipo di evento: clic, pressione dei tasti, passaggio del mouse, ...
    @param fn la funzione del gestore eventi
    @param isCapturing imposta la modalità evento (true = evento di acquisizione, false = evento di bubbling)
    @return true se il gestore eventi è stato collegato correttamente
    * /
    funzione addEvent (obj, evType, fn, isCapturing) {
      if (isCapturing == null) isCapturing = false; 
      if (obj.addEventListener) {
        // Firefox
        obj.addEventListener (evType, fn, isCapturing);
        ritorno vero;
      } else if (obj.attachEvent) {
        // MSIE
        var r = obj.attachEvent ('on' + evType, fn);
        ritorno r;
      } altro {
        restituire false;
      }
    }

    // registra la potenziale modifica della visibilità della pagina
    addEvent (documento, "entialvisilitychange ", funzione (evento) {
      document.getElementById ("x"). innerHTML + = "entialVisilityChange: potentialHidden = "+ document.potentialHidden +", document.potentiallyHiddenSince = "+ document.potentiallyHiddenSince +" s <br> ";
    });

    // registrati all'API di visibilità della pagina del W3C
    var nascosto = null;
    var visibleChange = 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 (nascosto! = null && visibleChange! = null) {
      addEvent (documento, visibilitàCambia, funzione (evento) {
        document.getElementById ("x"). innerHTML + = visibleChange + ":" + hidden + "=" + document [nascosto] + "<br>";
      });
    }


    var potenzialePageVisibility = {
      pageVisibilityChangeThreshold: 3 * 3600, // in secondi
      init: function () {
        function setAsNotHidden () {
          var dispatchEventRequired = document.potentialHidden;
          document.potentialHidden = false;
          document.potentiallyHiddenSince = 0;
          if (dispatchEventRequired) dispatchPageVisibilityChangeEvent ();
        }

        funzione initPotentiallyHiddenDetection () {
          if (! hasFocusLocal) {
            // la finestra non ha lo stato attivo => controlla l'attività dell'utente nella finestra
            lastActionDate = new Date ();
            if (timeoutHandler! = null) {
              clearTimeout (timeoutHandler);
            }
            timeoutHandler = setTimeout (checkPageVisibility ,entialPageVisibility.pageVisibilityChangeThreshold * 1000 + 100); // +100 ms per evitare problemi di arrotondamento in Firefox
          }
        }

        function dispatchPageVisibilityChangeEvent () {
          unifiedVisilityChangeEventDispatchAllowed = false;
          var evt = document.createEvent ("Event");
          evt.initEvent ("entialvisilitychange ", true, true);
          document.dispatchEvent (EVT);
        }

        function checkPageVisibility () {
          varentialHiddenDuration = (hasFocusLocal || lastActionDate == null? 0: Math.floor ((new Date (). getTime () - lastActionDate.getTime ()) / 1000));
                                        document.potentiallyHiddenSince = potentialHiddenDuration;
          if (entialHiddenDuration> = potentialPageVisibility.pageVisibilityChangeThreshold &&! document.potentialHidden) {
            // soglia di modifica della visibilità della pagina raiched => aumenta il pari
            document.potentialHidden = true;
            dispatchPageVisibilityChangeEvent ();
          }
        }

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

        addEvent (documento, "pageshow", funzione (evento) {
          document.getElementById ( "x") innerHTML + = "pageshow / doc: <br>.";
        });
        addEvent (documento, "pagehide", funzione (evento) {
          document.getElementById ( "x") innerHTML + = "pagehide / doc: <br>.";
        });
        addEvent (finestra, "pageshow", funzione (evento) {
          document.getElementById ( "x") innerHTML + = "pageshow / win: <br>."; // sollevato alla prima visualizzazione della pagina
        });
        addEvent (finestra, "pagehide", funzione (evento) {
          document.getElementById ( "x") innerHTML + = "pagehide / win: <br>."; // non sollevato
        });
        addEvent (documento, "mousemove", funzione (evento) {
          lastActionDate = new Date ();
        });
        addEvent (documento, "passaggio del mouse", funzione (evento) {
          hasMouseOver = true;
          setAsNotHidden ();
        });
        addEvent (documento, "mouseout", funzione (evento) {
          hasMouseOver = false;
          initPotentiallyHiddenDetection ();
        });
        addEvent (finestra, "sfocatura", funzione (evento) {
          hasFocusLocal = false;
          initPotentiallyHiddenDetection ();
        });
        addEvent (finestra, "focus", funzione (evento) {
          hasFocusLocal = true;
          setAsNotHidden ();
        });
        setAsNotHidden ();
      }
    }

    potentialPageVisibility.pageVisibilityChangeThreshold = 4; // 4 secondi per il test
    potentialPageVisibility.init ();
    </ Script>

Poiché al momento non esiste una soluzione cross-browser funzionante senza falsi positivi, è meglio pensarci due volte sulla disabilitazione dell'attività periodica sul proprio sito Web.


L'uso di un operatore di confronto rigoroso sulla stringa "non definito" anziché sulla parola chiave non definita causerebbe falsi positivi nel codice sopra?
Jonathon,

@kiran: attualmente funziona con Alt + Tab. Non è possibile determinare se la pagina è nascosta quando si esegue Alt + Tab perché è possibile passare a una finestra più piccola in modo da non poter garantire che la pagina sia completamente nascosta. Questo è il motivo per cui utilizzo il concetto di "potenzialmente nascosto" (nell'esempio, la soglia è impostata su 4 secondi, quindi è necessario passare a un'altra finestra usando Alt + Tab per almeno 4 secondi). Tuttavia, il tuo commento mostra che la risposta non era così chiara, quindi l'ho riformulata.
Julien Kronegg,

@JulienKronegg Penso che questa sia la soluzione migliore per ora. Tuttavia, il codice sopra ha estremamente bisogno di alcuni refactoring e astrazioni. Perché non lo carichi su GitHub e lasci che la community lo refactoring?
Jacob,

1
@Jacob Sono felice che ti sia piaciuta la mia soluzione. Sentiti libero di promuoverlo in un progetto GitHub da solo.
Fornisco

26

C'è una libreria ordinata disponibile su GitHub:

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

Esempio:

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

Ho testato la versione 1.0.1 su tutti i browser che ho e posso confermare che funziona con:

  • IE9, IE10
  • FF 26.0
  • Chrome 34.0

... e probabilmente tutte le versioni più recenti.

Non funziona completamente con:

  • IE8: indica sempre che la scheda / finestra è attualmente attiva ( .now()restituisce sempre trueper me)

La risposta accettata ha causato problemi in IE9. Questa libreria funziona alla grande.
Tom Teman,

20

Utilizzo: API di visibilità della pagina

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

Posso usare ? http://caniuse.com/#feat=pagevisibility


La domanda non riguarda la visibilità della pagina. Si tratta di non attivo / attivo
gman

Penso che OP non stia parlando della funzione di ide
l2aelba,

1
Non sto nemmeno parlando di ide. Sto parlando di alt-tabbing / cmd-tabbing con un'altra app. Improvvisamente la pagina non è attiva. L'API di visibilità della pagina non mi aiuta a sapere se la pagina non è attiva, mi aiuta solo a sapere se è possibilmente non visibile.
Gman,

18

Creo una Comet Chat per la mia app e quando ricevo un messaggio da un altro utente utilizzo:

if(new_message){
    if(!document.hasFocus()){
        audio.play();
        document.title="Have new messages";
    }
    else{
        audio.stop();
        document.title="Application Name";
    } 
}

2
La soluzione più pulita con supporto per IE6
Paul Cooper

4
document.hasFocus()è il modo più pulito per farlo. Tutti gli altri modi che utilizzano l'api di visibilità o l'evento basato o che cercano vari livelli di attività dell'utente / mancanza di attività diventano eccessivamente complicati e pieni di casi e buchi marginali. inseriscilo in un intervallo semplice e genera un evento personalizzato quando i risultati cambiano. Esempio: jsfiddle.net/59utucz6/1
danatcofo

1
Efficiente e diverso dalle altre soluzioni fornisce un feedback corretto quando si passa a un'altra scheda o finestra del browser e persino a un'applicazione diversa.
ow3n

Nessun dubbio, è il modo più pulito, ma non funziona in Firefox
Hardik Chugh,

1
Se apro gli strumenti Chrome Dev, document.hasFocus () è uguale a false. O anche se fai clic sul pannello superiore del browser, succede lo stesso. Non sono sicuro che questa soluzione sia adatta per mettere in pausa video, animazioni, ecc.
tylik,

16

Ho iniziato utilizzando la risposta wiki della community, ma mi sono reso conto che non stava rilevando eventi alt-tab in Chrome. Questo perché utilizza la prima fonte di eventi disponibile, e in questo caso è l'API di visibilità della pagina, che in Chrome sembra non tenere traccia della tabulazione alternativa.

Ho deciso di modificare un po 'lo script per tenere traccia di tutti gli eventi possibili per le modifiche al focus della pagina. Ecco una funzione che puoi inserire:

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;
};

Usalo in questo modo:

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

Questa versione è in ascolto di tutti i diversi eventi di visibilità e genera un callback se uno di essi provoca una modifica. I gestori focusede unfocusedassicurano che il callback non venga chiamato più volte se più API rilevano la stessa modifica di visibilità.


Chrome ad esempio ha sia document.hiddene document.webkitHidden. Senza l' elsenella ifcostruzione otterremmo 2 richiamate giuste?
Christiaan Westerbeek,

@ChristiaanWesterbeek È un buon punto, non ci ho pensato! Se puoi modificare questo post vai avanti e accetterò :)
Daniel Buckmaster il

Ehi, aspetta un minuto: la modifica per aggiungere "altro" suggerita da ChristiaanWesterbeek e effettivamente aggiunta da @ 1.21Gigawatt non sembra una buona idea: sconfigge l'acquisto originale dell'idea di Daniel, che è di provare tutti i supportati metodi in parallelo. E non vi è alcun rischio che il callback venga chiamato due volte perché focalizzato () e unfocused () sopprimono le chiamate extra quando non cambia nulla. Sembra davvero che dovremmo tornare al primo giro.
Louis Semprini,

@LouisSemprini è un'ottima cattura. Avevo dimenticato l'intento originale del codice! Ho ripristinato l'originale e ho aggiunto una spiegazione!
Daniel Buckmaster,

controllandolo da oggi, non rileva alt + tab almeno su Chrome 78 + macos
Hugo Gresse,

7

Questo è davvero complicato. Sembra che non ci sia soluzione dati i seguenti requisiti.

  • La pagina include iframe su cui non hai alcun controllo
  • Si desidera tenere traccia della modifica dello stato di visibilità indipendentemente dalla modifica attivata da una modifica TAB (ctrl + tab) o una modifica della finestra (alt + tab)

Questo accade perché:

  • L'API di visibilità della pagina può dirti in modo affidabile di un cambio di scheda (anche con iframe), ma non può dirti quando l'utente cambia Windows.
  • L'ascolto di eventi di sfocatura / messa a fuoco della finestra può rilevare alt + tab e ctrl + tab, purché l'iframe non abbia lo stato attivo.

Date queste restrizioni, è possibile implementare una soluzione che combina - L'API di visibilità della pagina - sfocatura / focus della finestra - document.activeElement

In grado di:

  • 1) ctrl + tab quando la pagina principale è attiva: SÌ
  • 2) ctrl + tab quando iframe è attivo: SÌ
  • 3) alt + tab quando la pagina principale è attiva: SÌ
  • 4) alt + tab quando iframe ha lo stato attivo: NO <- bummer

Quando l'iframe ha lo stato attivo, i tuoi eventi di sfocatura / stato attivo non vengono affatto richiamati e l'API di visibilità della pagina non si attiverà su alt + tab.

Ho sviluppato la soluzione di @ AndyE e implementato questa (quasi buona) soluzione qui: https://dl.dropboxusercontent.com/u/2683925/estante-components/visibility_test1.html (scusate, ho avuto qualche problema con JSFiddle).

Questo è disponibile anche su Github: https://github.com/qmagico/estante-components

Funziona su cromo / cromo. Funziona su firefox, tranne per il fatto che non carica i contenuti iframe (hai idea del perché?)

Ad ogni modo, per risolvere l'ultimo problema (4), l'unico modo per farlo è ascoltare eventi di sfocatura / messa a fuoco sull'iframe. Se hai un certo controllo sugli iframe, puoi usare l'API postMessage per farlo.

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

Non l'ho ancora provato con abbastanza browser. Se riesci a trovare maggiori informazioni su dove questo non funziona, per favore fatemi sapere nei commenti qui sotto.


Nei miei test ha funzionato anche su IE9, IE10 e Chrome su Android.
Tony Lâmpada,

1
Sembra IPAD bisogno di una soluzione completamente diversa - stackoverflow.com/questions/4940657/...
Tony Lâmpada

3
Tutti questi collegamenti sono 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/


5

questo funziona per me su Chrome 67, Firefox 67,

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

3

puoi usare:

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

})();


2

Questo è un adattamento della risposta di Andy E.

Ciò eseguirà un'attività, ad esempio aggiorna la pagina ogni 30 secondi, ma solo se la pagina è visibile e focalizzata.

Se la visibilità non può essere rilevata, verrà utilizzata solo la messa a fuoco.

Se l'utente focalizza la pagina, si aggiornerà immediatamente

La pagina non verrà aggiornata di nuovo fino a 30 secondi dopo qualsiasi chiamata 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);
}

Affidarsi ai metodi di messa a fuoco / sfocatura non funziona (ti dà molti falsi positivi), vedi
stackoverflow.com/a/9502074/698168

2

Per una soluzione senza jQuery, consulta Visibility.js che fornisce informazioni su tre stati di pagina

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

e anche wrapper per 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();
});

È disponibile anche un fallback per browser meno recenti (IE <10; iOS <7)


per quanto riguarda il supporto del browser? per ora biforcuta bene in Chrome, Safari e Firefox.
Selva Ganapathi,

1

Un modo leggermente più complicato sarebbe usare setInterval()per controllare la posizione del mouse e confrontarlo con l'ultimo controllo. Se il mouse non si è spostato per un determinato periodo di tempo, l'utente è probabilmente inattivo.

Questo ha l'ulteriore vantaggio di dire se l'utente è inattivo, invece di controllare solo se la finestra non è attiva.

Come molte persone hanno sottolineato, questo non è sempre un buon modo per verificare se l'utente o la finestra del browser sono inattivi, poiché l'utente potrebbe anche non utilizzare il mouse o guardare un video o simili. Sto solo suggerendo un modo possibile per verificare la pigrizia.


30
A meno che l'utente non abbia un mouse.
user1686


Anche questo non gioca a dadi se l'utente sta guardando un video
jamiew

è possibile utilizzare onkeypress o altri eventi simili per ripristinare il timer e risolvere il problema non del mouse. Ovviamente non funzionerebbe ancora per gli utenti che guardano attivamente la pagina per guardare un video, studiare un'immagine, ecc.
joshuahedlund,

1

Per angular.js, ecco una direttiva (basata sulla risposta accettata) che consentirà al controller di reagire a un cambiamento di visibilità:

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

});

Puoi usarlo come in questo esempio:, <div react-on-window-focus="refresh()">dove refresh()è una funzione scope nell'ambito di qualunque controller sia nell'ambito.


0

Ecco una soluzione solida e moderna. (Insomma un dolce 👌🏽)

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

Ciò imposterà un ascoltatore che si innescherà quando viene generato un evento di visibilità che potrebbe essere un focus o sfocatura.


0

Se si vuole agire su tutta la sfocatura del browser : Come ho commentato, se il browser perdere la concentrazione nessuno degli eventi del fuoco suggerito. La mia idea è di contare in un ciclo e azzerare il contatore in caso di incendio di un evento. Se il contatore raggiunge un limite, faccio un location.href in un'altra pagina. Questo funziona anche se lavori su dev-tools.

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

Questa è una bozza testata con successo su FF.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.