Czy istnieje sposób na wykrycie, czy okno przeglądarki nie jest aktualnie aktywne?

585

Mam JavaScript, który okresowo wykonuje czynności. Gdy użytkownik nie patrzy na stronę (tzn. Okno lub karta nie ma fokusa), fajnie byłoby nie uruchamiać.

Czy można to zrobić za pomocą JavaScript?

Mój punkt odniesienia: Czat Gmaila odtwarza dźwięk, jeśli okno, którego używasz, nie jest aktywne.

Luke Francl
źródło
8
Dla tych, którzy nie są zadowoleni z poniższych odpowiedzi, sprawdź requestAnimationFrameAPI lub skorzystaj z nowoczesnej funkcji zmniejszania częstotliwości setTimeout/, setIntervalgdy okno nie jest widoczne (na przykład 1 sekunda w Chrome).
Rob W
2
document.body.onblur = function (e) {console.log ('lama');} pracował dla nieostrych elementów.
WhyMe
2
Zobacz tę odpowiedź na rozwiązanie kompatybilne z wieloma przeglądarkami, które korzysta z interfejsu API Widoczności strony W3C, wracając do blur/ focusw przeglądarkach, które go nie obsługują.
Mathias Bynens,
2
80% poniższych odpowiedzi nie jest odpowiedzią na to pytanie . Pytanie dotyczy obecnie nieaktywnych, ale mnóstwo odpowiedzi poniżej jest niewidocznych, co nie jest odpowiedzią na to pytanie. Powinni być prawdopodobnie oznaczeni jako „nie odpowiedź”
gman

Odpowiedzi:

691

Od czasu napisania tej odpowiedzi nowa specyfikacja osiągnęła status rekomendacji dzięki W3C. Page Widoczność API (na MDN ) teraz pozwala nam bardziej precyzyjnie wykryć, gdy strona jest ukryta dla użytkownika.

document.addEventListener("visibilitychange", onchange);

Bieżąca obsługa przeglądarki:

  • Chrome 13+
  • Internet Explorer 10+
  • Firefox 10+
  • Opera 12.10+ [ czytaj notatki ]

Poniższy kod wraca do mniej niezawodnej metody rozmycia / ostrości w niekompatybilnych przeglądarkach:

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

onfocusini onfocusoutwymagane dla IE 9 i niższych , podczas gdy wszystkie inne korzystają z onfocusi onblur, z wyjątkiem iOS, który używa onpageshowi onpagehide.

Andy E.
źródło
1
@bellpeace: IE należy propagować focusini focusoutod iframe do górnego okna. W przypadku nowszych przeglądarek wystarczy obsłużyć zdarzenia focusi blurna windowobiekcie każdego elementu iframe . Powinieneś użyć zaktualizowanego kodu, który właśnie dodałem, który przynajmniej obejmie te przypadki w nowszych przeglądarkach.
Andy E
3
@JulienKronegg: dlatego moja odpowiedź wspomina o interfejsie API widoczności strony, który wszedł w status wersji roboczej po tym, jak pierwotnie napisałem swoją odpowiedź. Metody ustawiania ostrości / rozmycia zapewniają ograniczoną funkcjonalność starszych przeglądarek. Powiązanie z innymi zdarzeniami, jak w twojej odpowiedzi, nie obejmuje dużo więcej niż to i jest bardziej narażone na różnice behawioralne (np. IE nie uruchamia myszy, gdy wyskakuje okno pod kursorem). Sugeruję, że bardziej odpowiednim działaniem byłoby wyświetlenie wiadomości lub ikony wskazującej użytkownikowi, że aktualizacje mogą być rzadsze z powodu braku aktywności strony.
Andy E
6
@AndyE Wypróbowałem to rozwiązanie na chromie. Działa, jeśli zmieniam zakładki, ale nie działa, jeśli zmieniam okna (ALT + tab). Powinien? Oto skrzypce - jsfiddle.net/8a9N6/17
Tony Lâmpada
2
@Heliodor: Na razie chciałbym, aby kod w odpowiedzi był minimalny. Nigdy nie było zamierzone jako kompletne rozwiązanie typu „wklej i wklej”, ponieważ implementatorzy mogą chcieć uniknąć ustawiania klasy na ciele i podjąć zupełnie inną akcję (na przykład zatrzymanie i uruchomienie timera).
Andy E.
8
@AndyE Twoje rozwiązanie wydaje się działać tylko wtedy, gdy użytkownik zmieni zakładki lub zminimalizuje / zmaksymalizuje okno. Jednak zdarzenie onchange nie jest wyzwalane, jeśli użytkownik pozostawi kartę aktywną, ale maksymalizuje nad nią inny program z paska zadań. Czy istnieje rozwiązanie dla tego scenariusza? Dzięki!
user1491636
132

Użyłbym jQuery, ponieważ wtedy wszystko, co musisz zrobić, to:

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

A przynajmniej mi się udało.

Carson Wright
źródło
1
dla mnie to połączenie dwukrotnie w
ramce
W przeglądarce Firefox kliknięcie w konsoli Firebug (na tej samej stronie) windowspowoduje utratę fokusu, co jest słuszne, ale w zależności od tego, czego nie zamierzasz chcieć.
Majid Fouladpour
21
To nie działa już w aktualnych wersjach współczesnych przeglądarek, zobacz zatwierdzoną odpowiedź (API widoczności strony)
Jon z
To rozwiązanie nie działa na iPadzie, użyj wydarzenia „pagehow”
ElizaS
Zarówno BLUR, jak i FOCUS są uruchamiane podczas ładowania strony. Kiedy otwieram nowe okno z mojej strony, nic się nie dzieje, ale gdy nowe okno się zamyka, oba zdarzenia
odpalają
49

Istnieją 3 typowe metody określania, czy użytkownik może zobaczyć stronę HTML, jednak żadna z nich nie działa idealnie:

  • W3C API widoczności strony ma to zrobić (obsługiwany od: Firefox 10, MSIE 10, Chrome 13). Jednak ten interfejs API wywołuje zdarzenia tylko wtedy, gdy karta przeglądarki jest całkowicie przesłonięta (np. Gdy użytkownik przechodzi z jednej karty do drugiej). Interfejs API nie wywołuje zdarzeń, gdy nie można określić widoczności ze 100% dokładnością (np. Alt + Tab, aby przejść do innej aplikacji).

  • Stosowanie metod opartych na ogniskowaniu / rozmyciu daje wiele fałszywie pozytywnych wyników. Na przykład, jeśli użytkownik wyświetli mniejsze okno u góry okna przeglądarki, okno przeglądarki utraci fokus ( onblurpodniesiony), ale użytkownik nadal będzie mógł go zobaczyć (więc nadal trzeba go odświeżyć). Zobacz także http://javascript.info/tutorial/focus

  • Poleganie na aktywności użytkownika (poruszanie myszą, kliknięcia, pisanie na klawiaturze) daje również wiele fałszywych trafień. Pomyśl o tej samej sprawie jak powyżej lub o użytkowniku oglądającym film.

Aby poprawić opisane powyżej niedoskonałe zachowania, używam kombinacji 3 metod: API Widoczności W3C, następnie metody skupienia / rozmycia i aktywności użytkownika w celu zmniejszenia wskaźnika fałszywie dodatnich. Pozwala to zarządzać następującymi zdarzeniami:

  • Zmiana karty przeglądarki na inną (100% dokładności, dzięki interfejsowi API Widoczności strony W3C)
  • Strona potencjalnie ukryta przez inne okno, np. Z powodu Alt + Tab (probabilistyczna = nie w 100% dokładna)
  • Uwaga użytkownika potencjalnie nie koncentruje się na stronie HTML (probabilistic = nie w 100% dokładne)

Oto jak to działa: gdy dokument traci fokus, aktywność użytkownika (na przykład ruch myszy) na dokumencie jest monitorowana w celu ustalenia, czy okno jest widoczne, czy nie. Prawdopodobieństwo widoczności strony jest odwrotnie proporcjonalne do czasu ostatniej aktywności użytkownika na stronie: jeśli użytkownik nie wykonuje żadnej czynności w dokumencie przez dłuższy czas, strona najprawdopodobniej nie jest widoczna. Poniższy kod naśladuje interfejs API widoczności strony W3C: zachowuje się w ten sam sposób, ale ma niewielką liczbę wyników fałszywie dodatnich. Ma tę zaletę, że może być multibrowser (testowany na Firefox 5, Firefox 10, MSIE 9, MSIE 7, Safari 5, Chrome 9).

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

    <script>
    / **
    Rejestruje moduł obsługi zdarzenia dla danego obiektu.
    @param obj obiekt, który wywoła zdarzenie
    @param ev Wpisz typ zdarzenia: kliknięcie, naciśnięcie klawisza, kursor myszy, ...
    @param fn funkcja obsługi zdarzeń
    @param isCapturing ustaw tryb zdarzenia (true = przechwytywanie zdarzenia, false = propagowanie zdarzenia)
    @return true, jeśli moduł obsługi zdarzeń został poprawnie dołączony
    * /
    funkcja addEvent (obj, evType, fn, isCapturing) {
      if (isCapturing == null) isCapturing = false; 
      if (obj.addEventListener) {
        // Firefox
        obj.addEventListener (evType, fn, isCapturing);
        zwróć prawdę;
      } else if (obj.attachEvent) {
        // MSIE
        var r = obj.attachEvent ('on' + evType, fn);
        return r;
      } else {
        zwracać fałsz;
      }
    }

    // zarejestruj się do potencjalnej zmiany widoczności strony
    addEvent (dokument, „potencjałvisilitychange”, funkcja (zdarzenie) {
      document.getElementById ("x"). innerHTML + = "potencjałVisilityChange: potencjałHidden =" + document.potentialHidden + ", document.potentialHiddenSince =" + document.potentialHiddenSince + "s <br>";
    });

    // zarejestruj się w interfejsie API widoczności strony W3C
    var hidden = null;
    var visibilityChange = 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 (hidden! = null && visibilityChange! = null) {
      addEvent (dokument, widocznośćZmień, funkcja (zdarzenie) {
        document.getElementById ("x"). innerHTML + = visibilityChange + ":" + hidden + "=" + document [ukryty] + "<br>";
      });
    }


    var potencjałPageVisibility = {
      pageVisibilityChangeThreshold: 3 * 3600, // w sekundach
      init: function () {
        funkcja setAsNotHidden () {
          var dispatchEventRequired = document.potentialHidden;
          document.potentialHidden = false;
          document.potentialHiddenSince = 0;
          if (dispatchEventRequired) dispatchPageVisibilityChangeEvent ();
        }

        funkcja initPotentialHiddenDetection () {
          if (! hasFocusLocal) {
            // okno nie ma fokusa => sprawdź aktywność użytkownika w oknie
            lastActionDate = new Date ();
            if (timeoutHandler! = null) {
              clearTimeout (timeoutHandler);
            }
            timeoutHandler = setTimeout (checkPageVisibility, potencjałPageVisibility.pageVisibilityChangeThreshold * 1000 + 100); // +100 ms, aby uniknąć problemów z zaokrąglaniem w Firefoksie
          }
        }

        funkcja dispatchPageVisibilityChangeEvent () {
          unifiedVisilityChangeEventDispatchAllowed = false;
          var evt = document.createEvent („Event”);
          evt.initEvent („potencjałvisilitychange”, true, true);
          document.dispatchEvent (evt);
        }

        funkcja checkPageVisibility () {
          var potencjałHiddenDuration = (hasFocusLocal || lastActionDate == null? 0: Math.floor ((new Date (). getTime () - lastActionDate.getTime ()) / 1000));
                                        document.potentialHiddenSince = potencjałHiddenDuration;
          if (potencjałHiddenDuration> = potencjałPageVisibility.pageVisibilityChangeThreshold &&! document.potentialHidden) {
            // próg zmiany widoczności strony raiched => podnieść parzystość
            document.potentialHidden = true;
            dispatchPageVisibilityChangeEvent ();
          }
        }

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

        addEvent (dokument, „pokaz stron”, funkcja (zdarzenie) {
          document.getElementById ("x"). innerHTML + = "pageshow / doc: <br>";
        });
        addEvent (dokument, „strona”, funkcja (zdarzenie) {
          document.getElementById ("x"). innerHTML + = "pagehide / doc: <br>";
        });
        addEvent (okno, „przeglądanie stron”, funkcja (zdarzenie) {
          document.getElementById ("x"). innerHTML + = "pageshow / win: <br>"; // podniesiony, gdy strona pokazuje się po raz pierwszy
        });
        addEvent (okno, „pagehide”, funkcja (zdarzenie) {
          document.getElementById ("x"). innerHTML + = "pagehide / win: <br>"; // nie podniesiony
        });
        addEvent (dokument, „ruch myszy”, funkcja (zdarzenie) {
          lastActionDate = new Date ();
        });
        addEvent (dokument, „wskaźnik myszy”, funkcja (zdarzenie) {
          hasMouseOver = true;
          setAsNotHidden ();
        });
        addEvent (dokument, „mouseout”, funkcja (zdarzenie) {
          hasMouseOver = false;
          initPotentialHiddenDetection ();
        });
        addEvent (okno, „rozmycie”, funkcja (zdarzenie) {
          hasFocusLocal = false;
          initPotentialHiddenDetection ();
        });
        addEvent (okno, „fokus”, funkcja (zdarzenie) {
          hasFocusLocal = true;
          setAsNotHidden ();
        });
        setAsNotHidden ();
      }
    }

    potencjałPageVisibility.pageVisibilityChangeThreshold = 4; // 4 sekundy na testowanie
    potencjałPageVisibility.init ();
    </script>

Ponieważ obecnie nie ma działającego rozwiązania dla różnych przeglądarek bez fałszywego pozytywu, lepiej dwa razy pomyśleć o wyłączeniu okresowej aktywności na swojej stronie internetowej.

Julien Kronegg
źródło
Czy użycie ścisłego operatora porównania w ciągu „niezdefiniowany” zamiast niezdefiniowanego słowa kluczowego nie spowodowałoby fałszywych trafień w powyższym kodzie?
Jonathon,
@kiran: Właściwie to działa z Alt + Tab. Nie możesz ustalić, czy strona jest ukryta, gdy wykonujesz kombinację klawiszy Alt + Tab, ponieważ możesz przełączyć się na mniejsze okno, więc nie możesz zagwarantować, że strona jest całkowicie ukryta. Dlatego używam pojęcia „potencjalnie ukryty” (w tym przykładzie próg jest ustawiony na 4 sekundy, więc musisz przełączyć się do innego okna za pomocą Alt + Tab przez co najmniej 4 sekundy). Jednak twój komentarz pokazuje, że odpowiedź nie była tak jasna, więc przeredagowałem ją.
Julien Kronegg
@JulienKronegg Myślę, że to najlepsze jak dotąd rozwiązanie. Powyższy kod wymaga jednak bardzo refaktoryzacji i abstrakcji. Dlaczego nie prześlesz go do GitHub i nie pozwolisz społeczności go refaktoryzować?
Jacob
1
@Jacob Cieszę się, że polubiłeś moje rozwiązanie. Zachęcamy do samodzielnego promowania go w projekcie GitHub. Daję kod z licencją Creative Commons BY creativecommons.org/licenses/by/4.0
Julien Kronegg
26

Na GitHub dostępna jest zgrabna biblioteka:

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

Przykład:

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

Testowałem wersję 1.0.1 na wszystkich przeglądarkach, które posiadam i mogę potwierdzić, że działa ona z:

  • IE9, IE10
  • FF 26,0
  • Chrome 34.0

... i prawdopodobnie wszystkie nowsze wersje.

Nie działa w pełni z:

  • IE8 - zawsze wskazuje, że karta / okno jest aktualnie aktywne ( .now()zawsze zwraca truemi)
mniam mniam mniam
źródło
Zaakceptowana odpowiedź spowodowała problemy w IE9. Ta biblioteka działa świetnie.
Tom Teman
20

Używanie: API widoczności strony

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

Mogę uzyć ? http://caniuse.com/#feat=pagevisibility

L2aelba
źródło
Pytanie nie dotyczy widoczności strony. Chodzi o nieaktywny / aktywny
gman
Wydaje
1
Nie mówię też o ideach. Mówię o alt-tabbing / cmd-tabbing do innej aplikacji. Nagle strona nie jest aktywna. Interfejs API widoczności strony nie pomaga mi dowiedzieć się, czy strona nie jest aktywna, pomaga mi tylko wiedzieć, czy jest ona prawdopodobnie niewidoczna.
gman
18

Tworzę Czat Komety dla mojej aplikacji, a kiedy otrzymuję wiadomość od innego użytkownika, używam:

if(new_message){
    if(!document.hasFocus()){
        audio.play();
        document.title="Have new messages";
    }
    else{
        audio.stop();
        document.title="Application Name";
    } 
}
infinito84
źródło
2
Najczystsze rozwiązanie z obsługą z powrotem do IE6
Paul Cooper
4
document.hasFocus()jest najczystszym sposobem na zrobienie tego. Wszystkie inne sposoby korzystania z interfejsu widoczności lub zdarzenia opartego na zdarzeniu lub szukania różnych poziomów aktywności użytkownika / braku aktywności stają się nadmiernie skomplikowane i pełne przypadków i dziur na krawędziach. ustaw go w prostym odstępie czasu i wywołaj niestandardowe zdarzenie, gdy zmienią się wyniki. Przykład: jsfiddle.net/59utucz6/1
danatcofo
1
Wydajny iw przeciwieństwie do innych rozwiązań zapewnia prawidłowe informacje zwrotne po przełączeniu na inną kartę lub okno przeglądarki, a nawet inną aplikację.
ow3n
Bez wątpienia, to najczystszy sposób, ale nie działa w
Firefoksie
1
Jeśli otworzę narzędzia Chrome Dev, document.hasFocus () równa się false. Lub nawet jeśli klikniesz górny panel przeglądarki, to samo się stanie. Nie jestem pewien, czy to rozwiązanie nadaje się do wstrzymywania wideo, animacji itp.
tylik
16

Zacząłem od odpowiedzi na wiki społeczności, ale zdałem sobie sprawę, że nie wykrywa ona zdarzeń Alt-Tab w Chrome. Wynika to z faktu, że korzysta z pierwszego dostępnego źródła zdarzeń, aw tym przypadku jest to interfejs API widoczności strony, który w Chrome wydaje się nie śledzić tabulacji.

Postanowiłem trochę zmodyfikować skrypt, aby śledzić wszystkie możliwe zdarzenia związane ze zmianami fokusu strony. Oto funkcja, w którą możesz wpaść:

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

Użyj tego w ten sposób:

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

Ta wersja nasłuchuje wszystkich różnych zdarzeń widoczności i uruchamia oddzwonienie, jeśli którekolwiek z nich spowoduje zmianę. Programy obsługi focusedi unfocusedupewniają się, że wywołanie zwrotne nie zostanie wywołane wiele razy, jeśli wiele interfejsów API wykryje tę samą zmianę widoczności.

Daniel Buckmaster
źródło
Na przykład Chrome ma zarówno document.hiddeni document.webkitHidden. Bez elsew ifbudowie chcielibyśmy dostać 2 wywołań zwrotnych prawda?
Christiaan Westerbeek
@ChristiaanWesterbeek To dobra uwaga, nie myślałem o tym! Jeśli możesz edytować ten post, śmiało, a ja przyjmuję :)
Daniel Buckmaster
Hej, poczekaj chwilę: edycja dodawania „else” sugerowana przez ChristiaanWesterbeek i faktycznie dodana przez @ 1.21Gawawatts nie wydaje się dobrym pomysłem: pokonuje oryginalny zakup pomysłu Daniela, który polega na wypróbowaniu wszystkich obsługiwanych metody równoległe. I nie ma ryzyka, że ​​wywołanie zwrotne zostanie wywołane dwukrotnie, ponieważ focused () i unfocused () tłumią dodatkowe połączenia, gdy nic się nie zmienia. Naprawdę wydaje się, że powinniśmy powrócić do pierwszej wersji.
Louis Semprini
@LouisSemprini to świetny haczyk. Zapomniałem pierwotnej intencji kodu! Przywróciłem oryginał i dodałem wyjaśnienie!
Daniel Buckmaster
sprawdzanie tego na dzień dzisiejszy nie wykrywa Alt + Tab przynajmniej w Chrome 78 + Macos
Hugo Gresse
7

To jest naprawdę trudne. Wydaje się, że nie ma rozwiązania, biorąc pod uwagę następujące wymagania.

  • Strona zawiera elementy iframe, nad którymi nie masz kontroli
  • Chcesz śledzić zmianę stanu widoczności niezależnie od zmiany wywołanej zmianą TAB (ctrl + tab) lub zmianą okna (alt + tab)

Dzieje się tak, ponieważ:

  • Interfejs API widoczności strony może niezawodnie informować o zmianie karty (nawet w przypadku ramek iframe), ale nie może powiedzieć, kiedy użytkownik zmienia okno.
  • Słuchanie zdarzeń rozmycia / skupienia okna może wykryć klawisze alt + tab i ctrl + tab, o ile ramka iframe nie ma fokusa.

Biorąc pod uwagę te ograniczenia, możliwe jest wdrożenie rozwiązania, które łączy - Interfejs API widoczności strony - rozmycie / fokus okna - document.activeElement

To jest w stanie:

  • 1) ctrl + tab, gdy fokus znajduje się na stronie nadrzędnej: TAK
  • 2) ctrl + tab, gdy fokus iframe ma fokus: TAK
  • 3) Alt + Tab, gdy fokus ma stronę nadrzędną: TAK
  • 4) Alt + Tab, gdy fokus jest ustawiony na iframe: NIE <- bummer

Gdy ramka iframe jest aktywna, zdarzenia rozmycia / ostrości nie są w ogóle wywoływane, a interfejs API widoczności strony nie uruchamia się na klawiszach alt + tab.

Zbudowałem rozwiązanie @ AndyE i wdrożyłem to (prawie dobre) rozwiązanie tutaj: https://dl.dropboxusercontent.com/u/2683925/estante-components/visibility_test1.html (przepraszam, miałem pewne problemy z JSFiddle).

Jest to również dostępne na Github: https://github.com/qmagico/estante-components

Działa na chrom / chrom. To działa w Firefoksie, z wyjątkiem tego, że nie ładuje treści iframe (jakiś pomysł, dlaczego?)

W każdym razie, aby rozwiązać ostatni problem (4), jedynym sposobem, aby to zrobić, jest nasłuchiwanie zdarzeń rozmycia / skupienia w ramce iframe. Jeśli masz kontrolę nad ramkami iframe, możesz to zrobić za pomocą interfejsu API postMessage.

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

Nadal nie testowałem tego z wystarczającą liczbą przeglądarek. Jeśli możesz znaleźć więcej informacji o tym, gdzie to nie działa, daj mi znać w komentarzach poniżej.

Tony Lâmpada
źródło
W moich testach działał również na IE9, IE10 i Chrome na Androida.
Tony Lâmpada,
1
Wygląda na to, że IPAD potrzebuje zupełnie innego rozwiązania - stackoverflow.com/questions/4940657/...
Tony Lâmpada,
3
Wszystkie te linki to 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
źródło
5

to działa dla mnie na Chrome 67, Firefox 67,

if(!document.hasFocus()) {
    // do stuff
}
Samad
źródło
3

możesz użyć:

(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
źródło
3

W HTML 5 możesz także użyć:

  • onpageshow: Skrypt do uruchomienia, gdy okno stanie się widoczne
  • onpagehide: Skrypt do uruchomienia, gdy okno jest ukryte

Widzieć:

roberkules
źródło
2
Myślę, że jest to związane z BFCache: kiedy użytkownik kliknie Wstecz lub Dalej - nie jest to związane ze stroną znajdującą się na górze pulpitu komputera.
nonpolarity
2

To jest adaptacja odpowiedzi Andy'ego E.

Spowoduje to wykonanie zadania, np. Odświeżanie strony co 30 sekund, ale tylko wtedy, gdy strona jest widoczna i zogniskowana.

Jeśli nie można wykryć widoczności, zostanie użyty tylko fokus.

Jeśli użytkownik skoncentruje stronę, zostanie ona natychmiast zaktualizowana

Strona zaktualizuje się dopiero po 30 sekundach od jakiegokolwiek wywołania 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);
}
zrozumiałem
źródło
Poleganie na metodach skupienia / rozmycia nie działa (daje to dużo fałszywie pozytywnych wyników), patrz stackoverflow.com/a/9502074/698168
Julien Kronegg
2

Aby znaleźć rozwiązanie bez jQuery, sprawdź Visibility.js, który zawiera informacje o trzech stanach strony

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

a także owijarki dla 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();
});

Dostępna jest również funkcja rezerwowa dla starszych przeglądarek (IE <10; iOS <7)

Niko
źródło
co z obsługą przeglądarki? na razie rozwidlający się w Chrome, Safari i Firefox.
Selva Ganapathi
1

Nieco bardziej skomplikowanym sposobem byłoby użycie setInterval() sprawdzenie pozycji myszy i porównanie z ostatnim sprawdzaniem. Jeśli mysz nie poruszyła się w ustalonym czasie, użytkownik prawdopodobnie jest bezczynny.

Ma to tę dodatkową zaletę, że informuje, czy użytkownik jest bezczynny, a nie tylko sprawdzać, czy okno nie jest aktywne.

Jak wiele osób zauważyło, nie zawsze jest to dobry sposób na sprawdzenie, czy okno użytkownika lub przeglądarki jest bezczynne, ponieważ użytkownik może nawet nie korzystać z myszy, oglądać wideo lub podobne. Po prostu sugeruję jeden z możliwych sposobów sprawdzenia bezczynności.

Austin Hyde
źródło
30
Chyba że użytkownik nie ma myszy.
user1686
@Annan: Teraz codinghorror.com/blog/2007/03/… .
chiborg,
To również nie gra w kości, jeśli użytkownik ogląda film
jamiew
możesz użyć funkcji onkeypress lub innych podobnych zdarzeń, aby zresetować stoper i rozwiązać problem niezwiązany z myszą. Oczywiście nadal nie działałoby to dla użytkowników, którzy aktywnie patrzą na stronę, aby obejrzeć film, przestudiować obraz itp.
joshuahedlund,
1

W przypadku angular.js, oto dyrektywa (oparta na przyjętej odpowiedzi), która pozwoli twojemu kontrolerowi zareagować na zmianę widoczności:

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

});

Możesz użyć tego w następujący sposób: <div react-on-window-focus="refresh()">gdzie, gdzie refresh()jest funkcja zasięgu w zakresie dowolnego kontrolera, który jest w zasięgu.

Steve Campbell
źródło
0

Oto solidne, nowoczesne rozwiązanie. (Krótko słodko 👌🏽)

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

Spowoduje to skonfigurowanie nasłuchiwania do wyzwalania, gdy zostanie uruchomione dowolne zdarzenie widoczności, które może być skupieniem lub rozmyciem.

Cory Robinson
źródło
0

Jeśli chcesz reagować na rozmycie całej przeglądarki : tak jak skomentowałem, jeśli przeglądarka przestanie być aktywna, żadne z sugerowanych zdarzeń nie zostanie uruchomione. Moim pomysłem jest policzyć w pętli i zresetować licznik, jeśli nastąpi zdarzenie. Jeśli licznik osiągnie limit, robię lokalizację. Odsyłam do innej strony. To również strzela, jeśli pracujesz na narzędziach programistycznych.

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

Jest to projekt pomyślnie przetestowany na FF.

BF
źródło