C’è un modo per rilevare se una finestra del browser non è attualmente triggers?

Ho JavaScript che sta facendo attività periodicamente. Quando l’utente non sta guardando il sito (cioè, la finestra o la scheda non ha il focus), sarebbe bello non correre.

C’è un modo per farlo usando JavaScript?

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

Da quando ha originariamente 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.

Supporto browser corrente:

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

Il codice seguente utilizza l’API, ricadendo nel metodo sfocato / di 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"}); })(); 

onfocusin e onfocusout sono richiesti per IE 9 e onfocusin onfocusout , mentre tutti gli altri fanno uso di onfocus e onblur , ad eccezione di iOS, che utilizza onpageshow e onpagehide .

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.

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

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

  • Usando i metodi di messa a fuoco / sfocatura si ottengono 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 ( onblur sollevato) ma l’utente è ancora in grado di vederlo (quindi deve essere ancora aggiornato). Vedi anche http://javascript.info/tutorial/focus

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

Per migliorare i comportamenti imperfetti descritti sopra, 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. Questo consente di gestire i seguenti eventi:

  • Modifica della scheda del browser su un’altra (accuratezza al 100%, grazie all’API di visibilità della pagina W3C)
  • Pagina potenzialmente nascosta da un’altra finestra, ad esempio a causa di Alt + Tab (probabilistica = non accurata al 100%)
  • L’attenzione dell’utente potenzialmente non focalizzata sulla pagina HTML (probabilistica = non accurata al 100%)

Funziona così: quando il documento perde il focus, l’attività dell’utente (come il movimento 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à utente sulla pagina: se l’utente non fa attività sul documento per un lungo periodo, la pagina probabilmente non è visibile. Il codice seguente riproduce l’API di visibilità della pagina W3C: si comporta allo stesso modo ma ha un piccolo tasso di falsi positivi. Ha il vantaggio di essere multibrowser (testato su Firefox 5, Firefox 10, MSIE 9, MSIE 7, Safari 5, Chrome 9).

     

Poiché attualmente non esiste una soluzione cross-browser funzionante senza falsi positivi, dovresti pensare due volte meglio a disabilitare l’attività periodica sul tuo sito web.

C’è una biblioteca pulita disponibile su GitHub:

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

Esempio:

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

Ho provato 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 triggers ( .now() restituisce sempre true per me)

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

Questo è davvero difficile. Non sembra esserci alcuna soluzione dati i seguenti requisiti.

  • La pagina include iframe su cui non hai alcun controllo
  • Vuoi monitorare la modifica dello stato di visibilità indipendentemente dalla modifica che viene triggersta da una modifica TAB (ctrl + tab) o da una modifica della finestra (alt + tab)

Questo succede perché:

  • L’API Visibility della pagina ti può dire in modo affidabile di un cambio di tabulazione (anche con iframe), ma non può dirti quando l’utente cambia windows.
  • Ascoltando gli eventi sfocatura / messa a fuoco delle windows è ansible rilevare alt + tabs e ctrl + tabs, a condizione che l’iframe non abbia lo stato attivo.

Date queste restrizioni, è ansible implementare una soluzione che combini: l’API Visibility della pagina – sfocatura delle windows / focus – document.activeElement

Questo è in grado di:

  • 1) ctrl + tab quando la pagina padre ha lo stato attivo: YES
  • 2) ctrl + tab quando iframe ha lo stato attivo: YES
  • 3) alt + tab quando la pagina padre ha lo stato attivo: YES
  • 4) alt + tab quando iframe ha focus: NO <- bummer

Quando l’iframe è attivo, gli eventi sfocatura / fuoco non vengono richiamati affatto e l’API Visibility della pagina non viene triggersta in Alt + tab.

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

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

Funziona su cromo / cromo. Funziona su Firefox, eccetto per il fatto che non carica i contenuti iframe (qualche idea del perché?)

Ad ogni modo, per risolvere l’ultimo problema (4), l’unico modo per farlo è ascoltare gli eventi sfocatura / fuoco sull’iframe. Se hai il controllo sugli iframe, puoi usare l’API postMessage per farlo.

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

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

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

Ho iniziato a utilizzare la risposta wiki della community, ma mi sono reso conto che non rilevava 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 di alt-tabbing.

Ho deciso di modificare leggermente 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'); }); 
 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/

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

In HTML 5 puoi anche utilizzare:

  • onpageshow : script da eseguire quando la finestra diventa visibile
  • onpagehide : script da eseguire quando la finestra è nascosta

Vedere:

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

Questo ha il vantaggio di dire se l’utente è inattivo, invece di controllare se la finestra non è triggers.

Come molte persone hanno sottolineato, questo non è sempre un buon modo per verificare se l’utente o la finestra del browser è inattivo, in quanto l’utente potrebbe anche non utilizzare il mouse o sta guardando un video o simili. Sto solo suggerendo un modo ansible per verificare l’inattività.

Questo è un adattamento della risposta di Andy E.

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

Se non è ansible rilevare la visibilità, verrà utilizzata solo la messa a fuoco.

Se l’utente mette a fuoco la pagina, allora si aggiornerà immediatamente

La pagina non si aggiornerà di nuovo fino a 30 secondi dopo ogni 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 // eg 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); } 

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

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

e anche wrapper di convenienza 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 i browser più vecchi (IE <10; iOS <7)

Per angular.js, ecco una direttiva (basata sulla risposta accettata) che consentirà al controller di reactjs 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); }); } } } }; }); 

È ansible utilizzarlo come nell’esempio seguente:

, dove refresh() è una funzione di ambito nell’ambito di qualsiasi controller nell’ambito di scope.

questo funziona per me su chrome 67, firefox 67,

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

Se vuoi agire su tutta la sfocatura del browser : come ho commentato, se il browser non si concentra su nessuno degli eventi suggeriti dal fuoco. La mia idea è di fare il conto alla rovescia e azzerare il contatore in caso di incendio di un evento. Se il contatore raggiunge un limite, eseguo un location.href su un’altra pagina. Questo si triggers 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.

Volevo solo aggiungere: la domanda non è chiara scritta. “Quando l’utente non sta guardando il sito (cioè, la finestra o la scheda non ha il focus) …”

Posso guardare un sito quando non è attivo. La maggior parte dei sistemi desktop è in grado di mostrare windows in parallelo 🙂

Ecco perché l’API di visibilità della pagina è probabilmente la risposta giusta perché impedisce l’aggiornamento del sito quando “l’utente non è in grado di vedere gli aggiornamenti” che può essere molto diverso da “la scheda non ha il focus”.