Gibt es eine Möglichkeit zu erkennen, ob ein Browserfenster derzeit nicht aktiv ist?

585

Ich habe JavaScript, das regelmäßig Aktivitäten ausführt. Wenn der Benutzer die Site nicht betrachtet (dh das Fenster oder die Registerkarte hat keinen Fokus), wäre es schön, nicht ausgeführt zu werden.

Gibt es eine Möglichkeit, dies mit JavaScript zu tun?

Mein Bezugspunkt: Google Mail Chat gibt einen Ton wieder, wenn das von Ihnen verwendete Fenster nicht aktiv ist.

Luke Francl
quelle
8
Wenn Sie mit den folgenden Antworten nicht zufrieden sind, lesen Sie die requestAnimationFrameAPI oder verwenden Sie die moderne Funktion, mit der die Häufigkeit von setTimeout/ setIntervalverringert wird, wenn das Fenster nicht sichtbar ist (z. B. 1 Sekunde in Chrome).
Rob W
2
document.body.onblur = function (e) {console.log ('lama');} arbeitete für nicht fokussierte Elemente.
WhyMe
2
Sehen Sie diese Antwort für eine Cross-Browser - kompatible Lösung , dass Anwendungen des W3C Seite Visibility API, fallen zurück blur/ focusin Browsern , die es nicht unterstützen.
Mathias Bynens
2
80% der folgenden Antworten sind keine Antworten auf diese Frage . Die Frage fragt nach derzeit nicht aktiv, aber jede Menge Antworten unten sind nicht sichtbar, was keine Antwort auf diese Frage ist. Sie sollten wohl als "keine Antwort" gekennzeichnet werden
gman

Antworten:

691

Seit dem ursprünglichen Schreiben dieser Antwort hat eine neue Spezifikation dank des W3C den Empfehlungsstatus erreicht. Mit der Page Visibility API (auf MDN ) können wir jetzt genauer erkennen, wann eine Seite für den Benutzer ausgeblendet ist.

document.addEventListener("visibilitychange", onchange);

Aktuelle Browserunterstützung:

  • Chrome 13+
  • Internet Explorer 10+
  • Firefox 10+
  • Opera 12.10+ [ Notizen lesen ]

Der folgende Code greift auf die weniger zuverlässige Unschärfe- / Fokusmethode in inkompatiblen Browsern zurück:

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

onfocusinund onfocusoutsind für den Internet Explorer 9 und senken erforderlich , während alle anderen Gebrauch machen von onfocusund onblur, mit Ausnahme von iOS, welche Anwendungen onpageshowund onpagehide.

Andy E.
quelle
1
@bellpeace: IE sollte sich verbreiten focusinund focusoutvom Iframe zum oberen Fenster. Bei neueren Browsern müssten Sie nur die focusund blur-Ereignisse für das windowObjekt jedes Iframes verarbeiten . Sie sollten den aktualisierten Code verwenden, den ich gerade hinzugefügt habe und der zumindest diese Fälle in neueren Browsern abdeckt.
Andy E
3
@JulienKronegg: Aus diesem Grund wird in meiner Antwort ausdrücklich die Page Visibility API erwähnt, die nach dem ursprünglichen Schreiben meiner Antwort in den Status eines Arbeitsentwurfs eingetreten ist. Die Fokus- / Unschärfemethoden bieten eingeschränkte Funktionen für ältere Browser. Die Bindung an andere Ereignisse, wie in Ihrer Antwort, deckt nicht viel mehr ab und birgt ein höheres Risiko für Verhaltensunterschiede (z. B. wenn der IE kein Mouseout auslöst, wenn ein Fenster unter dem Cursor angezeigt wird). Ich würde vorschlagen, dass eine geeignetere Aktion darin besteht, eine Nachricht oder ein Symbol anzuzeigen, die dem Benutzer anzeigen, dass Aktualisierungen aufgrund von Seiteninaktivität weniger häufig sind.
Andy E
6
@AndyE Ich habe diese Lösung auf Chrom ausprobiert. Es funktioniert, wenn ich Registerkarten ändere, aber nicht, wenn ich Fenster ändere (ALT + Registerkarte). Sollte es? Hier ist eine Geige - jsfiddle.net/8a9N6/17
Tony Lâmpada
2
@Heliodor: Ich möchte den Code in der Antwort vorerst minimal halten. Es war nie als vollständige Lösung zum Ausschneiden und Einfügen gedacht, da Implementierer möglicherweise vermeiden möchten, eine Klasse für den Body festzulegen und eine völlig andere Aktion auszuführen (z. B. Stoppen und Starten eines Timers).
Andy E
8
@AndyE Ihre Lösung scheint nur zu funktionieren, wenn der Benutzer die Registerkarten wechselt oder das Fenster minimiert / maximiert. Das Ereignis onchange wird jedoch nicht ausgelöst, wenn der Benutzer die Registerkarte aktiv lässt, sondern ein anderes Programm über die Taskleiste maximiert. Gibt es eine Lösung für dieses Szenario? Vielen Dank!
user1491636
132

Ich würde jQuery verwenden, weil Sie dann nur noch Folgendes tun müssen:

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

Zumindest hat es bei mir funktioniert.

Carson Wright
quelle
1
für mich dieser Anruf zweimal in iframe
msangel
Wenn Sie in Firefox in die Firebug-Konsole (auf derselben Seite) klicken, verliert der windowFokus, was richtig ist, aber abhängig davon, was Ihre Absicht möglicherweise nicht das ist, was Sie brauchen.
Majid Fouladpour
21
Dies funktioniert nicht mehr für aktuelle Versionen moderner Browser, siehe die genehmigte Antwort (Page Visibility API)
Jon z
Diese Lösung funktioniert nicht auf dem iPad. Bitte verwenden Sie das Ereignis "pageshow"
ElizaS
Sowohl BLUR als auch FOCUS werden beim Laden der Seite ausgelöst. Wenn ich ein neues Fenster von meiner Seite öffne, passiert nichts, aber sobald das neue Fenster geschlossen ist, werden beide Ereignisse
ausgelöst
49

Es gibt drei typische Methoden, mit denen ermittelt wird, ob der Benutzer die HTML-Seite sehen kann. Keine davon funktioniert jedoch einwandfrei:

  • Die W3C Page Visibility API soll dies tun (unterstützt seit: Firefox 10, MSIE 10, Chrome 13). Diese API löst jedoch nur Ereignisse aus, wenn die Browser-Registerkarte vollständig überschrieben ist (z. B. wenn der Benutzer von einer Registerkarte zu einer anderen wechselt). Die API löst keine Ereignisse aus, wenn die Sichtbarkeit nicht mit 100% iger Genauigkeit bestimmt werden kann (z. B. Alt + Tab, um zu einer anderen Anwendung zu wechseln).

  • Die Verwendung von auf Fokus / Unschärfe basierenden Methoden führt zu vielen falsch positiven Ergebnissen. Wenn der Benutzer beispielsweise ein kleineres Fenster über dem Browserfenster anzeigt, verliert das Browserfenster den Fokus ( onblurangehoben), der Benutzer kann ihn jedoch weiterhin sehen (daher muss er noch aktualisiert werden). Siehe auch http://javascript.info/tutorial/focus

  • Wenn Sie sich auf Benutzeraktivitäten verlassen (Mausbewegung, Klicks, Eingabe der Taste), erhalten Sie auch viele falsch positive Ergebnisse. Stellen Sie sich den gleichen Fall wie oben vor oder einen Benutzer, der sich ein Video ansieht.

Um das oben beschriebene unvollständige Verhalten zu verbessern, verwende ich eine Kombination der drei Methoden: W3C-Sichtbarkeits-API, dann Fokus- / Unschärfe- und Benutzeraktivitätsmethoden, um die Falsch-Positiv-Rate zu reduzieren. Auf diese Weise können die folgenden Ereignisse verwaltet werden:

  • Ändern der Browser-Registerkarte in eine andere (100% Genauigkeit dank der W3C-API für Seitensichtbarkeit)
  • Seite möglicherweise von einem anderen Fenster ausgeblendet, z. B. aufgrund von Alt + Tab (probabilistisch = nicht 100% genau)
  • Die Aufmerksamkeit des Benutzers ist möglicherweise nicht auf die HTML-Seite gerichtet (probabilistisch = nicht 100% genau).

So funktioniert es: Wenn das Dokument den Fokus verliert, wird die Benutzeraktivität (z. B. Mausbewegung) des Dokuments überwacht, um festzustellen, ob das Fenster sichtbar ist oder nicht. Die Wahrscheinlichkeit der Seitensichtbarkeit ist umgekehrt proportional zum Zeitpunkt der letzten Benutzeraktivität auf der Seite: Wenn der Benutzer längere Zeit keine Aktivität im Dokument ausführt, ist die Seite höchstwahrscheinlich nicht sichtbar. Der folgende Code ahmt die W3C-Sichtbarkeits-API nach: Sie verhält sich genauso, weist jedoch eine geringe Falsch-Positiv-Rate auf. Es hat den Vorteil, Multibrowser zu sein (getestet auf Firefox 5, Firefox 10, MSIE 9, MSIE 7, Safari 5, Chrome 9).

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

    <script>
    / **
    Registriert den Handler für das Ereignis für das angegebene Objekt.
    @param obj das Objekt, das das Ereignis auslöst
    @param evTyp der Ereignistyp: Klicken, Tastendruck, Mouseover, ...
    @param fn die Event-Handler-Funktion
    @param isCapturing legt den Ereignismodus fest (true = Ereignis erfassen, false = sprudelndes Ereignis)
    @return true, wenn der Ereignishandler korrekt angehängt wurde
    * /
    Funktion addEvent (obj, evType, fn, isCapturing) {
      if (isCapturing == null) isCapturing = false; 
      if (obj.addEventListener) {
        // Feuerfuchs
        obj.addEventListener (evType, fn, isCapturing);
        return true;
      } else if (obj.attachEvent) {
        // MSIE
        var r = obj.attachEvent ('on' + evType, fn);
        return r;
      } else {
        falsch zurückgeben;
      }}
    }}

    // Registriere dich für die mögliche Änderung der Sichtbarkeit der Seite
    addEvent (Dokument, "Potentialvisilitychange", Funktion (Ereignis) {
      document.getElementById ("x"). innerHTML + = "PotentialVisilityChange: PotentialHidden =" + document.potentialHidden + ", document.potentialHiddenSince =" + document.potentialHiddenSince + "s <br>";
    });

    // Registrieren Sie sich bei der W3C Page Visibility API
    var hidden = null;
    var sichtbarkeitsänderung = null;
    if (typeof document.mozHidden! == "undefined") {
      hidden = "mozHidden";
      sichtbarkeitsänderung = "mozvisibilitychange";
    } else if (typeof document.msHidden! == "undefined") {
      hidden = "msHidden";
      sichtbarkeitsänderung = "msvisibilitychange";
    } else if (typeof document.webkitHidden! == "undefined") {
      hidden = "webkitHidden";
      sichtbarkeitsänderung = "webkitvisibilitychange";
    } else if (typeof document.hidden! == "hidden") {
      versteckt = "versteckt";
      sichtbarkeitsänderung = "sichtbarkeitsänderung";
    }}
    if (hidden! = null && sichtbarkeitsänderung! = null) {
      addEvent (Dokument, Sichtbarkeitsänderung, Funktion (Ereignis) {
        document.getElementById ("x"). innerHTML + = sichtbarkeitsänderung + ":" + hidden + "=" + document [hidden] + "<br>";
      });
    }}


    var PotentialPageVisibility = {
      pageVisibilityChangeThreshold: 3 * 3600, // in Sekunden
      init: function () {
        Funktion setAsNotHidden () {
          var dispatchEventRequired = document.potentialHidden;
          document.potentialHidden = false;
          document.potentialHiddenSince = 0;
          if (dispatchEventRequired) dispatchPageVisibilityChangeEvent ();
        }}

        Funktion initPotentialHiddenDetection () {
          if (! hasFocusLocal) {
            // Das Fenster hat nicht den Fokus => Auf Benutzeraktivität im Fenster prüfen
            lastActionDate = new Date ();
            if (timeoutHandler! = null) {
              clearTimeout (timeoutHandler);
            }}
            timeoutHandler = setTimeout (checkPageVisibility, PotentialPageVisibility.pageVisibilityChangeThreshold * 1000 + 100); // +100 ms, um Rundungsprobleme unter Firefox zu vermeiden
          }}
        }}

        Funktion dispatchPageVisibilityChangeEvent () {
          unifiedVisilityChangeEventDispatchAllowed = false;
          var evt = document.createEvent ("Ereignis");
          evt.initEvent ("potentielle Sichtbarkeitsänderung", wahr, wahr);
          document.dispatchEvent (evt);
        }}

        Funktion checkPageVisibility () {
          var PotentialHiddenDuration = (hasFocusLocal || lastActionDate == null? 0: Math.floor ((neues Datum (). getTime () - lastActionDate.getTime ()) / 1000));
                                        document.potentialHiddenSince = PotentialHiddenDuration;
          if (PotentialHiddenDuration> = PotentialPageVisibility.pageVisibilityChangeThreshold &&! document.potentialHidden) {
            // Schwellenwert für Änderung der Sichtbarkeit der Seite erhöht => Erhöhen Sie die Gerade
            document.potentialHidden = true;
            dispatchPageVisibilityChangeEvent ();
          }}
        }}

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

        addEvent (Dokument, "Pageshow", Funktion (Ereignis) {
          document.getElementById ("x"). innerHTML + = "pageshow / doc: <br>";
        });
        addEvent (Dokument, "Seitenhaut", Funktion (Ereignis) {
          document.getElementById ("x"). innerHTML + = "pagehide / doc: <br>";
        });
        addEvent (Fenster, "pageshow", Funktion (Ereignis) {
          document.getElementById ("x"). innerHTML + = "pageshow / win: <br>"; // wird ausgelöst, wenn die Seite zum ersten Mal angezeigt wird
        });
        addEvent (Fenster, "Seitenhaut", Funktion (Ereignis) {
          document.getElementById ("x"). innerHTML + = "pagehide / win: <br>"; // nicht ausgelöst
        });
        addEvent (Dokument, "Mausbewegung", Funktion (Ereignis) {
          lastActionDate = new Date ();
        });
        addEvent (Dokument, "Mouseover", Funktion (Ereignis) {
          hasMouseOver = true;
          setAsNotHidden ();
        });
        addEvent (Dokument, "Mouseout", Funktion (Ereignis) {
          hasMouseOver = false;
          initPotentialHiddenDetection ();
        });
        addEvent (Fenster, "Unschärfe", Funktion (Ereignis) {
          hasFocusLocal = false;
          initPotentialHiddenDetection ();
        });
        addEvent (Fenster, "Fokus", Funktion (Ereignis) {
          hasFocusLocal = true;
          setAsNotHidden ();
        });
        setAsNotHidden ();
      }}
    }}

    PotentialPageVisibility.pageVisibilityChangeThreshold = 4; // 4 Sekunden zum Testen
    PotentialPageVisibility.init ();
    </ script>

Da es derzeit keine funktionierende browserübergreifende Lösung ohne falsches Positiv gibt, sollten Sie zweimal überlegen, ob Sie regelmäßige Aktivitäten auf Ihrer Website deaktivieren möchten.

Julien Kronegg
quelle
Würde die Verwendung eines strengen Vergleichsoperators für die Zeichenfolge 'undefined' anstelle des undefinierten Schlüsselworts nicht zu Fehlalarmen im obigen Code führen?
Jonathon
@ Kiran: Eigentlich funktioniert es mit Alt + Tab. Sie können nicht feststellen, ob die Seite ausgeblendet ist, wenn Sie Alt + Tab ausführen, da Sie möglicherweise zu einem kleineren Fenster wechseln, sodass Sie nicht garantieren können, dass Ihre Seite vollständig ausgeblendet ist. Aus diesem Grund verwende ich den Begriff "möglicherweise ausgeblendet" (im Beispiel ist der Schwellenwert auf 4 Sekunden festgelegt, sodass Sie mindestens 4 Sekunden lang mit Alt + Tab zu einem anderen Fenster wechseln müssen). Ihr Kommentar zeigt jedoch, dass die Antwort nicht so klar war, also habe ich sie umformuliert.
Julien Kronegg
@ JulienKronegg Ich denke, dies ist die bisher beste Lösung. Der obige Code erfordert jedoch einige Umgestaltungen und Abstraktionen. Warum lädst du es nicht auf GitHub hoch und lässt es von der Community umgestalten?
Jacob
1
@ Jacob Ich bin froh, dass dir meine Lösung gefallen hat. Fühlen Sie sich frei, es selbst in ein GitHub-Projekt zu promoten. Ich gebe den Code mit Lizenz Creative Commons BY creativecommons.org/licenses/by/4.0
Julien Kronegg
26

Auf GitHub steht eine übersichtliche Bibliothek zur Verfügung:

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

Beispiel:

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

Ich habe Version 1.0.1 in allen Browsern getestet und kann bestätigen, dass es funktioniert mit:

  • IE9, IE10
  • FF 26.0
  • Chrome 34.0

... und wahrscheinlich alle neueren Versionen.

Funktioniert nicht vollständig mit:

  • IE8 - zeigt immer an, dass Tab / Fenster aktuell aktiv ist ( .now()kehrt immer truefür mich zurück)
OM Nom Nom
quelle
Akzeptierte Antwort verursachte Probleme in IE9. Diese Bibliothek funktioniert hervorragend.
Tom Teman
20

Verwenden von: Page Visibility API

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

Kann ich benutzen ? http://caniuse.com/#feat=pagevisibility

l2aelba
quelle
Die Frage betrifft nicht die Sichtbarkeit von Seiten. Es geht darum, nicht aktiv / aktiv zu sein
gman
Ich denke, OP spricht nicht über die Funktion von ide
l2aelba
1
Ich spreche auch nicht über Ideen. Ich spreche von Alt-Tabbing / Cmd-Tabbing zu einer anderen App. Plötzlich ist die Seite nicht mehr aktiv. Die API für die Sichtbarkeit von Seiten hilft mir nicht zu wissen, ob die Seite nicht aktiv ist, sondern nur, ob die Seite möglicherweise nicht sichtbar ist.
Gman
18

Ich erstelle einen Kometen-Chat für meine App und wenn ich eine Nachricht von einem anderen Benutzer erhalte, verwende ich:

if(new_message){
    if(!document.hasFocus()){
        audio.play();
        document.title="Have new messages";
    }
    else{
        audio.stop();
        document.title="Application Name";
    } 
}
infinito84
quelle
2
Die sauberste Lösung mit Unterstützung für IE6
Paul Cooper
4
document.hasFocus()ist der sauberste Weg, es zu tun. Alle anderen Möglichkeiten, die Sichtbarkeits-API oder ereignisbasiert zu verwenden oder nach verschiedenen Ebenen der Benutzeraktivität / mangelnder Aktivität zu suchen, werden überkompliziert und voller Randfälle und Lücken. Legen Sie ein einfaches Intervall fest und lösen Sie ein benutzerdefiniertes Ereignis aus, wenn sich die Ergebnisse ändern. Beispiel: jsfiddle.net/59utucz6/1
danatcofo
1
Effizient und im Gegensatz zu den anderen Lösungen erhalten Sie korrektes Feedback, wenn Sie zu einem anderen Browser-Tab oder -Fenster und sogar zu einer anderen Anwendung wechseln.
Ow3n
Kein Zweifel, es ist der sauberste Weg, aber es funktioniert nicht in Firefox
Hardik Chugh
1
Wenn ich Chrome Dev-Tools öffne, ist document.hasFocus () gleich false. Oder auch wenn Sie auf das obere Bedienfeld des Browsers klicken, geschieht dasselbe. Ich bin nicht sicher, ob diese Lösung geeignet ist, um Video, Animation usw.
anzuhalten
16

Ich habe zunächst die Antwort des Community-Wikis verwendet, aber festgestellt, dass in Chrome keine Alt-Tab-Ereignisse erkannt wurden. Dies liegt daran, dass die erste verfügbare Ereignisquelle verwendet wird. In diesem Fall handelt es sich um die API für die Sichtbarkeit von Seiten, die in Chrome anscheinend kein Alt-Tabbing erfasst.

Ich habe beschlossen, das Skript ein wenig zu ändern, um alle möglichen Ereignisse für Änderungen des Seitenfokus zu verfolgen . Hier ist eine Funktion, die Sie besuchen können:

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

Verwenden Sie es so:

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

Diese Version wartet auf alle verschiedenen Sichtbarkeitsereignisse und löst einen Rückruf aus, wenn eines davon eine Änderung verursacht. Die Handler focusedund unfocusedstellen sicher, dass der Rückruf nicht mehrmals aufgerufen wird, wenn mehrere APIs dieselbe Sichtbarkeitsänderung erfassen.

Daniel Buckmaster
quelle
Chrome zum Beispiel hat sowohl document.hiddenund document.webkitHidden. Ohne die elsein der ifKonstruktion würden wir 2 Rückrufe bekommen, oder?
Christiaan Westerbeek
@ChristiaanWesterbeek Das ist ein guter Punkt, daran habe ich nicht gedacht! Wenn Sie diesen Beitrag bearbeiten können, fahren Sie fort und ich werde akzeptieren :)
Daniel Buckmaster
Hey, warte mal: Die von ChristiaanWesterbeek vorgeschlagene und tatsächlich von @ 1.21Gigawatts hinzugefügte Bearbeitung zum Hinzufügen von "else" scheint keine gute Idee zu sein: Sie besiegt den ursprünglichen Kauf von Daniels Idee, alle unterstützten zu testen Methoden parallel. Und es besteht kein Risiko, dass der Rückruf zweimal aufgerufen wird, da fokussiert () und unfokussiert () zusätzliche Anrufe unterdrücken, wenn sich nichts ändert. Es scheint wirklich so, als sollten wir zur ersten Umdrehung zurückkehren.
Louis Semprini
@ LouisSemprini das ist ein toller Fang. Ich hatte die ursprüngliche Absicht des Codes vergessen! Ich habe das Original restauriert und eine Erklärung hinzugefügt!
Daniel Buckmaster
Wenn Sie dies ab heute überprüfen, wird alt + tab zumindest auf Chrome 78 + macos nicht erkannt
Hugo Gresse
7

Das ist wirklich schwierig. Angesichts der folgenden Anforderungen scheint es keine Lösung zu geben.

  • Die Seite enthält Iframes, über die Sie keine Kontrolle haben
  • Sie möchten die Änderung des Sichtbarkeitsstatus verfolgen, unabhängig davon, welche Änderung durch eine TAB-Änderung (Strg + Tab) oder eine Fensteränderung (Alt + Tab) ausgelöst wird.

Dies geschieht, weil:

  • Die Seiten-Sichtbarkeits-API kann Sie zuverlässig über eine Änderung der Registerkarte informieren (auch bei Iframes), aber nicht, wann der Benutzer die Fenster wechselt.
  • Durch das Abhören von Fensterunschärfe- / Fokusereignissen können Alt + Tabulatoren und Strg + Tabulatoren erkannt werden, solange der Iframe keinen Fokus hat.

Angesichts dieser Einschränkungen ist es möglich, eine Lösung zu implementieren, die Folgendes kombiniert: - Die API für Seitensichtbarkeit - Fensterunschärfe / Fokus - document.activeElement

Das kann:

  • 1) Strg + Tab, wenn die übergeordnete Seite den Fokus hat: JA
  • 2) Strg + Tab, wenn der Iframe den Fokus hat: JA
  • 3) Alt + Tab, wenn die übergeordnete Seite den Fokus hat: JA
  • 4) Alt + Tab, wenn Iframe den Fokus hat: NO <- Mist

Wenn der Iframe den Fokus hat, werden Ihre Unschärfe- / Fokusereignisse überhaupt nicht aufgerufen, und die Seiten-Sichtbarkeits-API wird auf Alt + Tab nicht ausgelöst.

Ich habe auf der Lösung von @ AndyE aufgebaut und diese (fast gute) Lösung hier implementiert: https://dl.dropboxusercontent.com/u/2683925/estante-components/visibility_test1.html (Entschuldigung, ich hatte einige Probleme mit JSFiddle).

Dies ist auch auf Github verfügbar: https://github.com/qmagico/estante-components

Dies funktioniert auf Chrom / Chrom. Es funktioniert auf Firefox, außer dass es den Iframe-Inhalt nicht lädt (eine Idee warum?)

Um das letzte Problem (4) zu lösen, können Sie dies nur tun, indem Sie auf Unschärfe- / Fokusereignisse im Iframe achten. Wenn Sie die iframes steuern können, können Sie dazu die postMessage-API verwenden.

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

Ich habe dies immer noch nicht mit genügend Browsern getestet. Wenn Sie weitere Informationen darüber finden, wo dies nicht funktioniert, lassen Sie es mich bitte in den Kommentaren unten wissen.

Tony Lâmpada
quelle
In meinen Tests funktionierte es auch unter IE9, IE10 und Chrome unter Android.
Tony Lâmpada
1
Es scheint, dass IPAD eine völlig andere Lösung benötigt - stackoverflow.com/questions/4940657/…
Tony Lâmpada
3
Alle diese Links sind 404s :(
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
quelle
5

Das funktioniert bei mir auf Chrom 67, Firefox 67,

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

Sie können verwenden:

(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
quelle
3

In HTML 5 können Sie auch Folgendes verwenden:

  • onpageshow: Skript, das ausgeführt wird, wenn das Fenster sichtbar wird
  • onpagehide: Skript, das ausgeführt werden soll, wenn das Fenster ausgeblendet ist

Sehen:

Roberkules
quelle
2
Ich denke, dies hängt mit dem BFCache zusammen: Wenn der Benutzer auf Zurück oder Vorwärts klickt, hängt dies nicht damit zusammen, dass sich die Seite oben auf dem Computer-Desktop befindet.
Unpolarität
2

Dies ist eine Anpassung der Antwort von Andy E.

Dadurch wird eine Aufgabe ausgeführt, z. B. die Seite alle 30 Sekunden aktualisiert, jedoch nur, wenn die Seite sichtbar und fokussiert ist.

Wenn keine Sichtbarkeit erkannt werden kann, wird nur der Fokus verwendet.

Wenn der Benutzer die Seite fokussiert, wird sie sofort aktualisiert

Die Seite wird erst 30 Sekunden nach einem Ajax-Aufruf erneut aktualisiert

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);
}
Roger
quelle
Sich auf Fokus- / Unschärfemethoden zu verlassen, funktioniert nicht (es gibt viele falsch positive Ergebnisse), siehe stackoverflow.com/a/9502074/698168
Julien Kronegg
2

Eine Lösung ohne jQuery finden Sie in Visibility.js, die Informationen zu drei Seitenzuständen enthält

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

und auch Convenience-Wrapper für 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();
});

Ein Fallback für ältere Browser (IE <10; iOS <7) ist ebenfalls verfügbar

Niko
quelle
Was ist mit der Browserunterstützung? vorerst gut in Chrom, Safari und Firefox.
Selva Ganapathi
1

Eine etwas kompliziertere Methode wäre die setInterval()Überprüfung der Mausposition und der Vergleich mit der letzten Überprüfung. Wenn sich die Maus in einer festgelegten Zeit nicht bewegt hat, ist der Benutzer wahrscheinlich untätig.

Dies hat den zusätzlichen Vorteil, dass Sie feststellen können, ob der Benutzer inaktiv ist, anstatt nur zu überprüfen, ob das Fenster nicht aktiv ist.

Wie viele Leute darauf hingewiesen haben, ist dies nicht immer eine gute Möglichkeit, um zu überprüfen, ob das Benutzer- oder Browserfenster inaktiv ist, da der Benutzer möglicherweise nicht einmal die Maus verwendet oder ein Video oder ähnliches ansieht. Ich schlage nur einen möglichen Weg vor, um den Leerlauf zu überprüfen.

Austin Hyde
quelle
30
Es sei denn, der Benutzer hat keine Maus.
user1686
@Annan: Es ist jetzt codinghorror.com/blog/2007/03/… .
Chiborg
Dies spielt auch keine Würfel, wenn der Benutzer ein Video sieht
Jamiew
Sie können onkeypress oder ähnliche Ereignisse verwenden, um den Timer zurückzusetzen und das Problem ohne Maus zu lösen. Natürlich würde es immer noch nicht funktionieren, wenn Benutzer aktiv auf die Seite schauen, um ein Video anzusehen, ein Bild zu studieren usw.
Joshuahedlund
1

Für angular.js gibt es hier eine Anweisung (basierend auf der akzeptierten Antwort), die es Ihrem Controller ermöglicht, auf eine Änderung der Sichtbarkeit zu reagieren:

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

});

Sie können es wie in diesem Beispiel verwenden: <div react-on-window-focus="refresh()">Wo refresh()befindet sich eine Bereichsfunktion im Bereich des Controllers ?

Steve Campbell
quelle
0

Hier ist eine solide, moderne Lösung. (Kurz ein süßes 👌🏽)

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

Dadurch wird ein Listener eingerichtet, der ausgelöst wird, wenn ein Sichtbarkeitsereignis ausgelöst wird, das ein Fokus oder eine Unschärfe sein kann.

Cory Robinson
quelle
0

Wenn Sie möchten , handeln , auf ganzen Browser blur : Wie ich bemerkte, wenn Browser verliert Fokus keines des Feuers vorgeschlagen Veranstaltungen. Meine Idee ist es, in einer Schleife hochzuzählen und den Zähler zurückzusetzen, wenn ein Ereignis ausgelöst wird. Wenn der Zähler ein Limit erreicht, mache ich einen location.href auf eine andere Seite. Dies wird auch ausgelöst, wenn Sie an Entwicklungswerkzeugen arbeiten.

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

Dies ist ein Entwurf, der erfolgreich auf FF getestet wurde.

BF
quelle