Wykryj, czy zakładka przeglądarki ma fokus

149

Czy istnieje niezawodny sposób wykrywania, czy fokus na karcie jest możliwy w różnych przeglądarkach?

Scenariusz jest taki, że mamy aplikację, która regularnie sonduje ceny akcji, a jeśli strona nie jest skupiona, moglibyśmy zatrzymać sondowanie i zaoszczędzić wszystkim hałas uliczny, zwłaszcza że ludzie są fanami otwierania kilku zakładek z różnymi portfelami.

Jest window.onbluri window.onfocusopcja do tego?

Fenton
źródło

Odpowiedzi:

127

Tak, window.onfocusi window.onblurpowinno działać w Twoim scenariuszu:

http://www.thefutureoftheweb.com/blog/detect-browser-window-focus

Ryan Wright
źródło
3
Aspekt onfocusin / onfocusout tego, a także uwaga o poinformowaniu użytkownika, że ​​wstrzymałeś pauzę, to naprawdę dobre notatki. Dzięki.
Fenton
7
Należy pamiętać, że w ten sposób nie można odróżnić strony aktywnej od nieaktywnej podczas ładowania strony.
pimvdb
@SteveFenton - onfocusto crossbrowser, w którym wydarzenia, o których wspomniałeś, dotyczą tylko IE, nie rozumiem, dlaczego miałoby to być uznane przez ciebie za dobrą notatkę ..
vsync
1
@vsync - przeczytaj link do artykułu, zobaczysz, że używa on zarówno „onfocusin”, jak i „onfocus”.
Fenton
Czy mógłbyś chociaż wspomnieć o różnicy między nimi?
Lenar Hoyt,
53

Ważna zmiana: ta odpowiedź jest nieaktualna. Od czasu jego napisania wprowadzono Visibility API ( mdn , przykład , specyfikacja ). To lepszy sposób na rozwiązanie tego problemu.


var focused = true;

window.onfocus = function() {
    focused = true;
};
window.onblur = function() {
    focused = false;
};

AFAIK, focusi blurwszystkie są obsługiwane na ... wszystkim. (patrz http://www.quirksmode.org/dom/events/index.html )

Zirak
źródło
2
Tylko mała uwaga, przy wszystkich tych rozwiązaniach ryzykujesz, że użytkownik zmieni zakładki przed pełnym załadowaniem javascript, przypisując w ten sposób niewłaściwą wartość do focus. Nie jestem pewien, czy istnieje dobry sposób na obejście tego problemu.
JayD3e
Linki do aktualizacji są dokładnie tym, czego szukałem. Dzięki za dodanie ich!
webLacky3rdClass
Pytanie dotyczy w szczególności wykrywania, czy fokus jest na stronie, co różni się od wykrywania, czy strona jest widoczna. Wiele stron może być widocznych w tym samym czasie (w różnych oknach), podczas gdy tylko jedna może mieć fokus. Użyj dowolnej techniki, która odpowiada Twoim potrzebom, ale pamiętaj o różnicy.
jaredjacobs
1
Jest to niebezpieczne rozwiązanie, ponieważ stwarza ryzyko zastąpienia innego nasłuchiwania zdarzeń w większej aplikacji. Zamiast tego powinieneś postępować zgodnie z tą odpowiedzią: stackoverflow.com/a/21935031/549503
mmmeff
51

Szukając informacji o tym problemie, znalazłem zalecenie, aby skorzystać z API Page Visibility API . Większość nowoczesnych przeglądarek obsługuje ten interfejs API zgodnie z Can I Use: http://caniuse.com/#feat=pagevisibility .

Oto działający przykład (pochodzący z tego fragmentu ):

$(document).ready(function() {
  var hidden, visibilityState, visibilityChange;

  if (typeof document.hidden !== "undefined") {
    hidden = "hidden", visibilityChange = "visibilitychange", visibilityState = "visibilityState";
  } else if (typeof document.msHidden !== "undefined") {
    hidden = "msHidden", visibilityChange = "msvisibilitychange", visibilityState = "msVisibilityState";
  }

  var document_hidden = document[hidden];

  document.addEventListener(visibilityChange, function() {
    if(document_hidden != document[hidden]) {
      if(document[hidden]) {
        // Document hidden
      } else {
        // Document shown
      }

      document_hidden = document[hidden];
    }
  });
});

Aktualizacja: powyższy przykład miał właściwości z prefiksem dla przeglądarek Gecko i WebKit, ale usunąłem tę implementację, ponieważ te przeglądarki od jakiegoś czasu oferują interfejs API widoczności strony bez prefiksu. Zachowałem specyficzny prefiks Microsoft, aby zachować zgodność z IE10.

Ilija
źródło
Kiedy prefiksy dostawców znikną z tego, prawdopodobnie zmienię się!
Fenton,
Jedynym prawdziwym problemem nie są prefiksy dostawców, ponieważ istnieje oficjalna rekomendacja W3C (z 29. października 2013). W niektórych przypadkach problemem jest to, że API widoczności strony jest obsługiwane w IE10 i nowszych. Jeśli potrzebujesz obsługi IE9, powinieneś poszukać innego podejścia…
Ilija,
To jest właściwy sposób na zrobienie tego we wszystkich nowoczesnych przeglądarkach. +1
Ajedi32
Czy na pewno te prefiksy dostawców są w ogóle konieczne? Według MDN i CanIUse nie były one potrzebne w Chrome od wersji 32, ani w Firefoksie od wersji 17 i nigdy nie były konieczne w IE.
Ajedi, 32
@ Ajedi32 Thanks. Będę musiał przeprowadzić kilka testów i kopać, aby zobaczyć, co jest nadal aktualne, a co można teraz pominąć.
Ilija
37

Zaskakujące, że nikt nie wspomniano document.hasFocus

if (document.hasFocus()) console.log('Tab is active')

MDN ma więcej informacji.

aleclarson
źródło
działa u mnie (testowane na Chrome i Firefox). Przyjęta odpowiedź (onfocus / onblur) nie zadziałała
harmv
Prawidłowa odpowiedź jeszcze raz na samym dole. Tak trzymaj StackOverflow!
11 października
naprawdę, czy to nie jest idealna odpowiedź? czy ktoś widzi jakieś minusy?
gaspar
2
Jedynym minusem jest to, że jeśli próbujesz określić, czy karta jest aktywna z wewnątrz elementu iframe, to zakończy się niepowodzeniem, jeśli element iframe został załadowany, gdy strona nadrzędna była nadal nieostra. Aby to również uwzględnić, musisz skorzystać z interfejsu API widoczności strony.
Ivan
29

Tak, to powinno działać dla Ciebie. Właśnie przypomniałeś mi o linku, na który natknąłem się, który wykorzystuje te techniki. ciekawa lektura

Brian Glaz
źródło
2
+1 - to bardzo sprytna sztuczka, mogę sobie wyobrazić, że oszukuje wielu ludzi.
Fenton
2
Cóż za genialny i przebiegły atak. Ciekawie to przeczytać, dzięki.
Voo,
4

Zrobiłbym to w ten sposób (źródło http://www.w3.org/TR/page-visibility/ ):

    window.onload = function() {

        // check the visiblility of the page
        var hidden, visibilityState, visibilityChange;

        if (typeof document.hidden !== "undefined") {
            hidden = "hidden", visibilityChange = "visibilitychange", visibilityState = "visibilityState";
        }
        else if (typeof document.mozHidden !== "undefined") {
            hidden = "mozHidden", visibilityChange = "mozvisibilitychange", visibilityState = "mozVisibilityState";
        }
        else if (typeof document.msHidden !== "undefined") {
            hidden = "msHidden", visibilityChange = "msvisibilitychange", visibilityState = "msVisibilityState";
        }
        else if (typeof document.webkitHidden !== "undefined") {
            hidden = "webkitHidden", visibilityChange = "webkitvisibilitychange", visibilityState = "webkitVisibilityState";
        }


        if (typeof document.addEventListener === "undefined" || typeof hidden === "undefined") {
            // not supported
        }
        else {
            document.addEventListener(visibilityChange, function() {
                console.log("hidden: " + document[hidden]);
                console.log(document[visibilityState]);

                switch (document[visibilityState]) {
                case "visible":
                    // visible
                    break;
                case "hidden":
                    // hidden
                    break;
                }
            }, false);
        }

        if (document[visibilityState] === "visible") {
            // visible
        }

    };  
konfile
źródło
Czy możesz wyjaśnić, w jaki sposób ta odpowiedź różni się od odpowiedzi udzielonej przez @Ilija - może być różnica, ale jest subtelna - więc mile widziane byłoby wyjaśnienie, co to jest i dlaczego powinno być inne.
Fenton,
2

Rozwiązanie jQuery dla wielu przeglądarek! Raw dostępne w GitHub

Zabawne i łatwe w użyciu!

Następująca wtyczka przejdzie przez standardowy test dla różnych wersji IE, Chrome, Firefox, Safari itp. I odpowiednio ustali zadeklarowane metody. Zajmuje się również takimi kwestiami, jak:

  • onblur | .blur / onfocus | .focus „ zduplikowane ” wywołania
  • okno traci fokus poprzez wybór alternatywnej aplikacji, takiej jak Word
    • Wydaje się to być niepożądane po prostu dlatego, że jeśli masz otwartą stronę banku i zdarzenie onblur nakazuje jej maskowanie strony, to jeśli otworzysz kalkulator, nie możesz już zobaczyć strony!
  • Nie uruchamia się po załadowaniu strony

Używanie jest tak proste, jak: Przewiń w dół do „ Uruchom fragment

$.winFocus(function(event, isVisible) {
    console.log("Combo\t\t", event, isVisible);
});

//  OR Pass False boolean, and it will not trigger on load,
//  Instead, it will first trigger on first blur of current tab_window
$.winFocus(function(event, isVisible) {
    console.log("Combo\t\t", event, isVisible);
}, false);

//  OR Establish an object having methods "blur" & "focus", and/or "blurFocus"
//  (yes, you can set all 3, tho blurFocus is the only one with an 'isVisible' param)
$.winFocus({
    blur: function(event) {
        console.log("Blur\t\t", event);
    },
    focus: function(event) {
        console.log("Focus\t\t", event);
    }
});

//  OR First method becoms a "blur", second method becoms "focus"!
$.winFocus(function(event) {
    console.log("Blur\t\t", event);
},
function(event) {
    console.log("Focus\t\t", event);
});

/*    Begin Plugin    */
;;(function($){$.winFocus||($.extend({winFocus:function(){var a=!0,b=[];$(document).data("winFocus")||$(document).data("winFocus",$.winFocus.init());for(x in arguments)"object"==typeof arguments[x]?(arguments[x].blur&&$.winFocus.methods.blur.push(arguments[x].blur),arguments[x].focus&&$.winFocus.methods.focus.push(arguments[x].focus),arguments[x].blurFocus&&$.winFocus.methods.blurFocus.push(arguments[x].blurFocus),arguments[x].initRun&&(a=arguments[x].initRun)):"function"==typeof arguments[x]?b.push(arguments[x]):
"boolean"==typeof arguments[x]&&(a=arguments[x]);b&&(1==b.length?$.winFocus.methods.blurFocus.push(b[0]):($.winFocus.methods.blur.push(b[0]),$.winFocus.methods.focus.push(b[1])));if(a)$.winFocus.methods.onChange()}}),$.winFocus.init=function(){$.winFocus.props.hidden in document?document.addEventListener("visibilitychange",$.winFocus.methods.onChange):($.winFocus.props.hidden="mozHidden")in document?document.addEventListener("mozvisibilitychange",$.winFocus.methods.onChange):($.winFocus.props.hidden=
"webkitHidden")in document?document.addEventListener("webkitvisibilitychange",$.winFocus.methods.onChange):($.winFocus.props.hidden="msHidden")in document?document.addEventListener("msvisibilitychange",$.winFocus.methods.onChange):($.winFocus.props.hidden="onfocusin")in document?document.onfocusin=document.onfocusout=$.winFocus.methods.onChange:window.onpageshow=window.onpagehide=window.onfocus=window.onblur=$.winFocus.methods.onChange;return $.winFocus},$.winFocus.methods={blurFocus:[],blur:[],focus:[],
exeCB:function(a){$.winFocus.methods.blurFocus&&$.each($.winFocus.methods.blurFocus,function(b,c){this.apply($.winFocus,[a,!a.hidden])});a.hidden&&$.winFocus.methods.blur&&$.each($.winFocus.methods.blur,function(b,c){this.apply($.winFocus,[a])});!a.hidden&&$.winFocus.methods.focus&&$.each($.winFocus.methods.focus,function(b,c){this.apply($.winFocus,[a])})},onChange:function(a){var b={focus:!1,focusin:!1,pageshow:!1,blur:!0,focusout:!0,pagehide:!0};if(a=a||window.event)a.hidden=a.type in b?b[a.type]:
document[$.winFocus.props.hidden],$(window).data("visible",!a.hidden),$.winFocus.methods.exeCB(a);else try{$.winFocus.methods.onChange.call(document,new Event("visibilitychange"))}catch(c){}}},$.winFocus.props={hidden:"hidden"})})(jQuery);
/*    End Plugin      */

// Simple example
$(function() {
	$.winFocus(function(event, isVisible) {
		$('td tbody').empty();
		$.each(event, function(i) {
			$('td tbody').append(
				$('<tr />').append(
					$('<th />', { text: i }),
					$('<td />', { text: this.toString() })
				)
			)
		});
		if (isVisible) 
			$("#isVisible").stop().delay(100).fadeOut('fast', function(e) {
				$('body').addClass('visible');
				$(this).stop().text('TRUE').fadeIn('slow');
			});
		else {
			$('body').removeClass('visible');
			$("#isVisible").text('FALSE');
		}
	});
})
body { background: #AAF; }
table { width: 100%; }
table table { border-collapse: collapse; margin: 0 auto; width: auto; }
tbody > tr > th { text-align: right; }
td { width: 50%; }
th, td { padding: .1em .5em; }
td th, td td { border: 1px solid; }
.visible { background: #FFA; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<h3>See Console for Event Object Returned</h3>
<table>
    <tr>
        <th><p>Is Visible?</p></th>
        <td><p id="isVisible">TRUE</p></td>
    </tr>
    <tr>
        <td colspan="2">
            <table>
                <thead>
                    <tr>
                        <th colspan="2">Event Data <span style="font-size: .8em;">{ See Console for More Details }</span></th>
                    </tr>
                </thead>
                <tbody></tbody>
            </table>
        </td>
    </tr>
</table>

SpYk3HH
źródło
Kod wtyczki powinien być niezminimalizowany.
Patrick Desjardins
@PatrickDesjardins yeah. Zaplanuj to w ten weekend wraz z innymi rzeczami. JA? Zrób podsumowanie kilku rzeczy, które mam. Jdmckinstry na github. Dodam linki do starych odpowiedzi, takich jak te, gdy zostaną dodane do
sedna
Co się stanie, jeśli chcę, aby strona przestała być aktywna po przełączeniu się do innej aplikacji, np. „Word” lub „Kalkulator”?
Benas
@Benas Może się mylić, ale uważam, że jest to podstawowa funkcjonalność bardzo podstawowej jQuery(window).blur/focus, która była niepożądana przez wielu, a więc jeden z powodów, dla których stworzyłem tę wtyczkę. Wtyczka ma pomóc w zapewnieniu tego, czego jQuery jeszcze nie zawiera
SpYk3HH