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?


Odpowiedzi:


127

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

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


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 )


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.


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.


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


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
        }

    };  

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>


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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.