Existe-t-il un moyen de détecter si une fenêtre de navigateur n'est pas actuellement active?

585

J'ai JavaScript qui fait régulièrement de l'activité. Lorsque l'utilisateur ne regarde pas le site (c'est-à-dire que la fenêtre ou l'onglet n'a pas le focus), ce serait bien de ne pas s'exécuter.

Existe-t-il un moyen de le faire en utilisant JavaScript?

Mon point de référence: Gmail Chat émet un son si la fenêtre que vous utilisez n'est pas active.

Luke Francl
la source
8
Pour ceux qui ne sont pas satisfaits des réponses ci-dessous, consultez l' requestAnimationFrameAPI ou utilisez la fonctionnalité moderne selon laquelle la fréquence de setTimeout/ setIntervalest réduite lorsque la fenêtre n'est pas visible (1 seconde dans Chrome, par exemple).
Rob W
2
document.body.onblur = function (e) {console.log ('lama');} fonctionnait pour les éléments non focalisés.
WhyMe
2
Consultez cette réponse pour une solution compatible avec tous les navigateurs qui utilise l'API de visibilité des pages du W3C, se repliant sur blur/ focusdans les navigateurs qui ne la prennent pas en charge.
Mathias Bynens
2
80% des réponses ci - dessous ne sont pas des réponses à cette question . La question demande non actuellement actif mais des tonnes de réponses ci-dessous ne sont pas visibles ce qui n'est pas une réponse à cette question. Ils devraient sans doute être signalés comme "pas une réponse"
gman
La plupart des gens parlent de non actif quand ils veulent dire non actif et non visible . Tout simplement pas actif est facile - il suffit de gérer les fenêtres blur/ focusévénements ... qui seront cependant d'une utilité limitée, car une fenêtre peut être inactive mais entièrement ou partiellement visible (il y a aussi des icônes de "prévisualisation" dans certaines barres de tâches que les gens s'attendent à continuer à être) mise à jour).
rustyx il y a

Réponses:

691

Depuis la rédaction initiale de cette réponse, une nouvelle spécification a atteint le statut de recommandation grâce au W3C. L' API de visibilité des pages (sur MDN ) nous permet désormais de détecter plus précisément lorsqu'une page est masquée pour l'utilisateur.

document.addEventListener("visibilitychange", onchange);

Prise en charge actuelle du navigateur:

  • Chrome 13+
  • Internet Explorer 10+
  • Firefox 10+
  • Opera 12.10+ [ lire les notes ]

Le code suivant revient à la méthode de flou / focus moins fiable dans les navigateurs incompatibles:

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

onfocusinet onfocusoutsont requis pour IE 9 et inférieur , tandis que tous les autres utilisent onfocuset onblur, à l'exception d'iOS, qui utilise onpageshowet onpagehide.

Andy E
la source
1
@bellpeace: IE devrait se propager focusinet focusoutde l'iframe à la fenêtre supérieure. Pour les nouveaux navigateurs, il vous suffirait de gérer les événements focuset blursur chaque windowobjet iframe . Vous devez utiliser le code mis à jour que je viens d'ajouter, qui couvrira au moins ces cas dans les nouveaux navigateurs.
Andy E
3
@JulienKronegg: c'est pourquoi ma réponse mentionne spécifiquement l'API de visibilité de la page qui est entrée dans l'état de brouillon de travail après avoir écrit ma réponse à l'origine. Les méthodes de focus / flou offrent des fonctionnalités limitées pour les navigateurs plus anciens. La liaison à d'autres événements, comme dans votre réponse, ne couvre pas beaucoup plus que cela et est plus à risque de différences de comportement (comme IE ne tirant pas avec la souris lorsqu'une fenêtre apparaît sous le curseur). Je suggérerais qu'une action plus appropriée serait d'afficher un message ou une icône indiquant à l'utilisateur que les mises à jour peuvent être moins fréquentes en raison de l'inactivité de la page.
Andy E
6
@AndyE J'ai essayé cette solution sur le chrome. Cela fonctionne si je change d'onglet, mais pas si je change de fenêtre (ALT + tab). Devrait-il? Voici un violon - jsfiddle.net/8a9N6/17
Tony Lâmpada
2
@ Heliodor: Je voudrais garder le code dans la réponse minimal pour l'instant. Il n'a jamais été conçu pour être une solution complète de copier-coller, car les implémenteurs pourraient vouloir éviter de définir une classe sur le corps et prendre une action complètement différente (comme arrêter et démarrer une minuterie).
Andy E
8
@AndyE Votre solution ne semble fonctionner que si l'utilisateur change d'onglet ou minimise / maximise la fenêtre. Toutefois, l'événement onchange n'est pas déclenché si l'utilisateur laisse l'onglet actif, mais maximise un autre programme par-dessus depuis la barre des tâches. Y a-t-il une solution pour ce scénario? Merci!
user1491636
132

J'utiliserais jQuery car alors tout ce que vous avez à faire est le suivant:

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

Ou du moins, cela a fonctionné pour moi.

Carson Wright
la source
1
pour moi cet appel deux fois dans iframe
msangel
Dans Firefox, si vous cliquez à l'intérieur de la console Firebug (sur la même page), le windowfocus sera perdu, ce qui est vrai, mais selon ce que votre intention pourrait ne pas être ce dont vous avez besoin.
Majid Fouladpour
21
Cela ne fonctionne plus pour les versions actuelles des navigateurs modernes, voir la réponse approuvée (API Page Visibility)
Jon z
Cette solution ne fonctionne pas sur iPad, veuillez utiliser l'événement "pageshow"
ElizaS
BLUR et FOCUS se déclenchent au chargement de la page. Lorsque j'ouvre une nouvelle fenêtre à partir de ma page, rien ne se passe mais une fois la nouvelle fenêtre fermée, les deux événements se déclenchent: / (en utilisant IE8)
SearchForKnowledge
49

Il existe 3 méthodes typiques utilisées pour déterminer si l'utilisateur peut voir la page HTML, mais aucune ne fonctionne parfaitement:

  • L' API de visibilité de page du W3C est censée faire cela (prise en charge depuis: Firefox 10, MSIE 10, Chrome 13). Cependant, cette API ne déclenche des événements que lorsque l'onglet du navigateur est entièrement remplacé (par exemple lorsque l'utilisateur passe d'un onglet à un autre). L'API ne déclenche pas d'événements lorsque la visibilité ne peut pas être déterminée avec une précision de 100% (par exemple Alt + Tab pour passer à une autre application).

  • L'utilisation de méthodes basées sur le focus / flou vous donne beaucoup de faux positifs. Par exemple, si l'utilisateur affiche une fenêtre plus petite en haut de la fenêtre du navigateur, la fenêtre du navigateur perdra le focus ( onblursurélevée) mais l'utilisateur est toujours en mesure de la voir (il doit donc être actualisé). Voir aussi http://javascript.info/tutorial/focus

  • S'appuyer sur l'activité de l'utilisateur (déplacement de la souris, clics, frappe au clavier) vous donne également beaucoup de faux positifs. Pensez au même cas que ci-dessus, ou à un utilisateur qui regarde une vidéo.

Afin d'améliorer les comportements imparfaits décrits ci-dessus, j'utilise une combinaison des 3 méthodes: API de visibilité W3C, puis focus / flou et méthodes d'activité utilisateur afin de réduire le taux de faux positifs. Cela permet de gérer les événements suivants:

  • Changer l'onglet du navigateur en un autre (précision de 100%, grâce à l'API W3C Page Visibility)
  • Page potentiellement masquée par une autre fenêtre, par exemple en raison de Alt + Tab (probabiliste = pas 100% précis)
  • L'attention des utilisateurs n'est peut-être pas concentrée sur la page HTML (probabiliste = pas précis à 100%)

Voici comment cela fonctionne: lorsque le document perd le focus, l'activité de l'utilisateur (comme le déplacement de la souris) sur le document est surveillée afin de déterminer si la fenêtre est visible ou non. La probabilité de visibilité de la page est inversement proportionnelle à l'heure de la dernière activité de l'utilisateur sur la page: si l'utilisateur ne fait aucune activité sur le document pendant une longue période, la page n'est probablement pas visible. Le code ci-dessous imite l'API W3C Page Visibility: il se comporte de la même manière mais a un petit taux de faux positifs. Il a l'avantage d'être multibrowser (testé sur Firefox 5, Firefox 10, MSIE 9, MSIE 7, Safari 5, Chrome 9).

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

    <script>
    / **
    Enregistre le gestionnaire de l'événement pour l'objet donné.
    @param obj l'objet qui déclenchera l'événement
    @param evType le type d'événement: clic, pression de touche, survol de la souris, ...
    @param fn la fonction du gestionnaire d'événements
    @param isCapturing définit le mode d'événement (true = événement de capture, false = événement de propagation)
    @return true si le gestionnaire d'événements a été correctement attaché
    * /
    fonction addEvent (obj, evType, fn, isCapturing) {
      if (isCapturing == null) isCapturing = false; 
      if (obj.addEventListener) {
        // Firefox
        obj.addEventListener (evType, fn, isCapturing);
        return true;
      } else if (obj.attachEvent) {
        // MSIE
        var r = obj.attachEvent ('on' + evType, fn);
        return r;
      } autre {
        retour faux;
      }
    }

    // s'inscrire au changement potentiel de visibilité de page
    addEvent (document, "potentialvisilitychange", fonction (événement) {
      document.getElementById ("x"). innerHTML + = "potentialVisilityChange: potentialHidden =" + document.potentialHidden + ", document.potentialHiddenSince =" + document.potentialHiddenSince + "s <br>";
    });

    // s'inscrire à l'API de visibilité de page du W3C
    var caché = null;
    var visibilitéChange = null;
    if (typeof document.mozHidden! == "undefined") {
      hidden = "mozHidden";
      visibilitéChange = "mozvisibilitychange";
    } else if (typeof document.msHidden! == "undefined") {
      hidden = "msHidden";
      visibilitéChange = "msvisibilitychange";
    } else if (typeof document.webkitHidden! == "undefined") {
      hidden = "webkitHidden";
      visibilitéChange = "webkitvisibilitychange";
    } else if (typeof document.hidden! == "hidden") {
      caché = "caché";
      visibilitéChange = "visibilitéchange";
    }
    if (caché! = null && visibilitéChange! = null) {
      addEvent (document, visibilitéChange, fonction (événement) {
        document.getElementById ("x"). innerHTML + = visibilitéChange + ":" + caché + "=" + document [caché] + "<br>";
      });
    }


    var potentialPageVisibility = {
      pageVisibilityChangeThreshold: 3 * 3600, // en secondes
      init: fonction () {
        fonction setAsNotHidden () {
          var dispatchEventRequired = document.potentialHidden;
          document.potentialHidden = false;
          document.potentialHiddenSince = 0;
          if (dispatchEventRequired) dispatchPageVisibilityChangeEvent ();
        }

        fonction initPotentialHiddenDetection () {
          if (! hasFocusLocal) {
            // la fenêtre n'a pas le focus => vérifier l'activité des utilisateurs dans la fenêtre
            lastActionDate = new Date ();
            if (timeoutHandler! = null) {
              clearTimeout (timeoutHandler);
            }
            timeoutHandler = setTimeout (checkPageVisibility, potentialPageVisibility.pageVisibilityChangeThreshold * 1000 + 100); // +100 ms pour éviter les problèmes d'arrondi sous Firefox
          }
        }

        function dispatchPageVisibilityChangeEvent () {
          unifiedVisilityChangeEventDispatchAllowed = false;
          var evt = document.createEvent ("Event");
          evt.initEvent ("potentialvisilitychange", vrai, vrai);
          document.dispatchEvent (evt);
        }

        function checkPageVisibility () {
          var potentialHiddenDuration = (hasFocusLocal || lastActionDate == null? 0: Math.floor ((new Date (). getTime () - lastActionDate.getTime ()) / 1000));
                                        document.potentialHiddenSince = potentialHiddenDuration;
          if (potentialHiddenDuration> = potentialPageVisibility.pageVisibilityChangeThreshold &&! document.potentialHidden) {
            // seuil de changement de visibilité de la page raiched => augmenter le pair
            document.potentialHidden = true;
            dispatchPageVisibilityChangeEvent ();
          }
        }

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

        addEvent (document, "pageshow", fonction (événement) {
          document.getElementById ("x"). innerHTML + = "pageshow / doc: <br>";
        });
        addEvent (document, "pagehide", fonction (événement) {
          document.getElementById ("x"). innerHTML + = "pagehide / doc: <br>";
        });
        addEvent (fenêtre, "pageshow", fonction (événement) {
          document.getElementById ("x"). innerHTML + = "pageshow / win: <br>"; // levé lorsque la page s'affiche pour la première fois
        });
        addEvent (fenêtre, "pagehide", fonction (événement) {
          document.getElementById ("x"). innerHTML + = "pagehide / win: <br>"; // non levé
        });
        addEvent (document, "mousemove", fonction (événement) {
          lastActionDate = new Date ();
        });
        addEvent (document, "survol de la souris", fonction (événement) {
          hasMouseOver = true;
          setAsNotHidden ();
        });
        addEvent (document, "mouseout", fonction (événement) {
          hasMouseOver = false;
          initPotiallyHiddenDetection ();
        });
        addEvent (fenêtre, "flou", fonction (événement) {
          hasFocusLocal = false;
          initPotiallyHiddenDetection ();
        });
        addEvent (fenêtre, "focus", fonction (événement) {
          hasFocusLocal = true;
          setAsNotHidden ();
        });
        setAsNotHidden ();
      }
    }

    potentialPageVisibility.pageVisibilityChangeThreshold = 4; // 4 secondes pour les tests
    potentialPageVisibility.init ();
    </script>

Puisqu'il n'y a actuellement aucune solution multi-navigateur qui fonctionne sans faux positif, vous devriez mieux réfléchir à deux fois avant de désactiver l'activité périodique sur votre site Web.

Julien Kronegg
la source
L'utilisation d'un opérateur de comparaison strict sur la chaîne «non définie» au lieu du mot clé non défini ne provoquerait-elle pas de faux positifs dans le code ci-dessus?
Jonathon
@kiran: En fait, cela fonctionne avec Alt + Tab. Vous ne pouvez pas déterminer si la page est masquée lorsque vous effectuez un Alt + Tab, car vous pouvez basculer vers une fenêtre plus petite, vous ne pouvez donc pas garantir que votre page est entièrement masquée. C'est pourquoi j'utilise la notion de "potentiellement caché" (dans l'exemple, le seuil est fixé à 4 secondes, vous devez donc passer à une autre fenêtre en utilisant Alt + Tab pendant au moins 4 secondes). Cependant, votre commentaire montre que la réponse n'était pas si claire, je l'ai donc reformulée.
Julien Kronegg
@JulienKronegg Je pense que c'est la meilleure solution à ce jour. Cependant, le code ci-dessus a extrêmement besoin de refactorisation et d'abstractions. Pourquoi ne pas le télécharger sur GitHub et laisser la communauté le refactoriser?
Jacob
1
@Jacob Je suis content que vous ayez aimé ma solution. N'hésitez pas à le promouvoir vous-même dans un projet GitHub. Je donne le code avec licence Creative Commons BY creativecommons.org/licenses/by/4.0
Julien Kronegg
26

Il y a une bibliothèque soignée disponible sur GitHub:

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

Exemple:

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

J'ai testé la version 1.0.1 sur tous les navigateurs que j'ai et je peux confirmer qu'elle fonctionne avec:

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

... et probablement toutes les versions plus récentes.

Ne fonctionne pas entièrement avec:

  • IE8 - indique toujours que l'onglet / la fenêtre est actuellement actif ( .now()revient toujours truepour moi)
Om Nom Nom
la source
La réponse acceptée a causé des problèmes dans IE9. Cette bibliothèque fonctionne très bien.
Tom Teman
20

Utilisation: API de visibilité de page

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

Puis-je utiliser ? http://caniuse.com/#feat=pagevisibility

l2aelba
la source
La question ne concerne pas la visibilité des pages. Il s'agit de non actif / actif
gman
Je pense que OP ne parle pas de la fonction d'ide
l2aelba
1
Je ne parle pas non plus d'idé. Je parle de tabulation alt / tabulation cmd vers une autre application. Soudain, la page n'est pas active. L'API de visibilité de la page ne m'aide pas à savoir si la page n'est pas active, elle m'aide seulement à savoir si elle n'est peut-être pas visible.
gman
18

Je crée un chat Comet pour mon application et lorsque je reçois un message d'un autre utilisateur, j'utilise:

if(new_message){
    if(!document.hasFocus()){
        audio.play();
        document.title="Have new messages";
    }
    else{
        audio.stop();
        document.title="Application Name";
    } 
}
infinito84
la source
2
La solution la plus propre avec un support de retour à IE6
Paul Cooper
4
document.hasFocus()est le moyen le plus propre de le faire. Toutes les autres façons d'utiliser l'API de visibilité ou basée sur un événement ou de rechercher différents niveaux d'activité / manque d'activité de l'utilisateur deviennent trop compliquées et remplies de cas et de trous de bord. placez-le sur un intervalle simple et déclenchez un événement personnalisé lorsque les résultats changent. Exemple: jsfiddle.net/59utucz6/1
danatcofo
1
Efficace et contrairement aux autres solutions, donne un retour correct lorsque vous passez à un autre onglet ou fenêtre de navigateur, et même à une autre application.
ow3n
Pas de doute, c'est la manière la plus propre, mais cela ne fonctionne pas dans Firefox
Hardik Chugh
1
Si j'ouvre les outils Chrome Dev, document.hasFocus () est égal à false. Ou même si vous cliquez sur le panneau supérieur du navigateur, la même chose se produit. Je ne suis pas sûr que cette solution soit adaptée pour mettre en pause la vidéo, l'animation, etc.
tylik
16

J'ai commencé à utiliser la réponse wiki communautaire, mais j'ai réalisé qu'elle ne détectait pas les événements alt-tab dans Chrome. En effet, il utilise la première source d'événement disponible, et dans ce cas, c'est l'API de visibilité des pages, qui dans Chrome ne semble pas suivre les tabulations alt.

J'ai décidé de modifier un peu le script pour garder une trace de tous les événements possibles pour les changements de focus de page. Voici une fonction que vous pouvez ajouter:

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

Utilisez-le comme ceci:

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

Cette version écoute tous les différents événements de visibilité et déclenche un rappel si l'un d'eux provoque un changement. Les gestionnaires focusedet unfocuseds'assurent que le rappel n'est pas appelé plusieurs fois si plusieurs API interceptent le même changement de visibilité.

Daniel Buckmaster
la source
Chrome, par exemple, a les deux document.hiddenet document.webkitHidden. Sans elsedans la ifconstruction, nous recevrions 2 rappels, n'est-ce pas?
Christiaan Westerbeek
@ChristiaanWesterbeek C'est un bon point, je n'y ai pas pensé! Si vous pouvez modifier ce message, allez-y et j'accepterai :)
Daniel Buckmaster
Euh hé, attendez une minute: la modification pour ajouter des "autres" suggérée par ChristiaanWesterbeek et effectivement ajoutée par @ 1.21Gigawatts ne semble pas être une bonne idée: elle annule l'achat initial de l'idée de Daniel, qui est d'essayer tous les supports méthodes en parallèle. Et il n'y a aucun risque que le rappel soit appelé deux fois car les fonctions focus () et unfocused () suppriment les appels supplémentaires lorsque rien ne change. Il semble vraiment que nous devrions revenir à la première rév.
Louis Semprini
@LouisSemprini c'est une bonne prise. J'avais oublié l'intention originale du code! J'ai restauré l'original et ajouté une explication!
Daniel Buckmaster
vérifiant cela à partir d'aujourd'hui, il ne détecte pas alt + tab au moins sur Chrome 78 + macos
Hugo Gresse
7

C'est vraiment délicat. Il ne semble pas y avoir de solution compte tenu des exigences suivantes.

  • La page contient des iframes sur lesquels vous n'avez aucun contrôle
  • Vous souhaitez suivre le changement d'état de visibilité indépendamment du changement déclenché par un changement TAB (ctrl + tab) ou un changement de fenêtre (alt + tab)

Cela se produit parce que:

  • L'API de visibilité de page peut vous informer de manière fiable d'un changement d'onglet (même avec des iframes), mais elle ne peut pas vous dire quand l'utilisateur change de fenêtre.
  • L'écoute des événements de flou / focus de la fenêtre peut détecter les tabulations alt + et ctrl + tab, tant que l'iframe n'a pas le focus.

Compte tenu de ces restrictions, il est possible d'implémenter une solution qui combine - l'API de visibilité des pages - le flou / focus de la fenêtre - document.activeElement

Cela est capable de:

  • 1) ctrl + tab lorsque la page parent a le focus: OUI
  • 2) ctrl + tab lorsque iframe a le focus: OUI
  • 3) alt + tab lorsque la page parent a le focus: OUI
  • 4) alt + tab quand iframe a le focus: NO <- bummer

Lorsque l'iframe a le focus, vos événements flou / focus ne sont pas du tout invoqués et l'API de visibilité de la page ne se déclenche pas sur alt + tab.

J'ai construit sur la solution de @ AndyE et mis en œuvre cette (presque bonne) solution ici: https://dl.dropboxusercontent.com/u/2683925/estante-components/visibility_test1.html (désolé, j'ai eu quelques problèmes avec JSFiddle).

Ceci est également disponible sur Github: https://github.com/qmagico/estante-components

Cela fonctionne sur le chrome / chrome. Il fonctionne en quelque sorte sur Firefox, sauf qu'il ne charge pas le contenu iframe (une idée pourquoi?)

Quoi qu'il en soit, pour résoudre le dernier problème (4), la seule façon de le faire est d'écouter les événements de flou / focus sur l'iframe. Si vous avez un certain contrôle sur les iframes, vous pouvez utiliser l'API postMessage pour ce faire.

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

Je n'ai toujours pas testé cela avec suffisamment de navigateurs. Si vous pouvez trouver plus d'informations sur les cas où cela ne fonctionne pas, veuillez me le faire savoir dans les commentaires ci-dessous.

Tony Lâmpada
la source
Dans mes tests, il a également fonctionné sur IE9, IE10 et Chrome sur Android.
Tony Lâmpada
1
Il semble que l'IPAD ait besoin d'une solution complètement différente - stackoverflow.com/questions/4940657/…
Tony Lâmpada
3
Tous ces liens sont des 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
la source
5

cela fonctionne pour moi sur chrome 67, firefox 67,

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

vous pouvez utiliser:

(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
la source
3

En HTML 5, vous pouvez également utiliser:

  • onpageshow: Script à exécuter lorsque la fenêtre devient visible
  • onpagehide: Script à exécuter lorsque la fenêtre est masquée

Voir:

roberkules
la source
2
Je pense que cela est lié au BFCache: lorsque l'utilisateur clique sur Précédent ou Suivant - ce n'est pas lié à la page se trouvant en haut du bureau de l'ordinateur.
non
2

Ceci est une adaptation de la réponse d'Andy E.

Cela fera une tâche, par exemple actualiser la page toutes les 30 secondes, mais uniquement si la page est visible et ciblée.

Si la visibilité ne peut pas être détectée, seule la mise au point sera utilisée.

Si l'utilisateur concentre la page, celle-ci sera mise à jour immédiatement

La page ne se mettra à jour que 30 secondes après tout appel 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);
}
Roger
la source
S'appuyer sur les méthodes de mise au point / flou ne fonctionne pas (cela vous donne beaucoup de faux positifs), voir stackoverflow.com/a/9502074/698168
Julien Kronegg
2

Pour une solution sans jQuery, consultez Visibility.js qui fournit des informations sur les états de trois pages

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

et aussi des wrappers pratiques pour 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();
});

Une solution de rechange pour les navigateurs plus anciens (IE <10; iOS <7) est également disponible

Niko
la source
qu'en est-il du support du navigateur? pour l'instant bifurquer bien dans Chrome, Safari et Firefox.
Selva Ganapathi
1

Une manière un peu plus compliquée serait d'utiliser setInterval()pour vérifier la position de la souris et comparer avec la dernière vérification. Si la souris n'a pas bougé dans un laps de temps défini, l'utilisateur est probablement inactif.

Cela a l'avantage supplémentaire de dire si l'utilisateur est inactif, au lieu de simplement vérifier si la fenêtre n'est pas active.

Comme de nombreuses personnes l'ont souligné, ce n'est pas toujours un bon moyen de vérifier si la fenêtre de l'utilisateur ou du navigateur est inactive, car l'utilisateur peut même ne pas utiliser la souris ou regarder une vidéo, ou similaire. Je suggère simplement une façon possible de vérifier l'inactivité.

Austin Hyde
la source
30
Sauf si l'utilisateur n'a pas de souris.
user1686
@Annan: C'est codinghorror.com/blog/2007/03/… maintenant.
chiborg
Cela ne joue pas non plus aux dés si l'utilisateur regarde une vidéo
jamiew
vous pouvez utiliser onkeypress ou d'autres événements similaires pour réinitialiser le minuteur et résoudre le problème non lié à la souris. Bien sûr, cela ne fonctionnerait toujours pas pour les utilisateurs qui regardent activement la page pour regarder une vidéo, étudier une image, etc.
joshuahedlund
1

Pour angular.js, voici une directive (basée sur la réponse acceptée) qui permettra à votre contrôleur de réagir à un changement de visibilité:

myApp.directive('reactOnWindowFocus', function($parse) {
    return {
        restrict: "A",
        link: function(scope, element, attrs) {
            var hidden = "hidden";
            var currentlyVisible = true;
            var functionOrExpression = $parse(attrs.reactOnWindowFocus);

          // Standards:
          if (hidden in document)
            document.addEventListener("visibilitychange", onchange);
          else if ((hidden = "mozHidden") in document)
            document.addEventListener("mozvisibilitychange", onchange);
          else if ((hidden = "webkitHidden") in document)
            document.addEventListener("webkitvisibilitychange", onchange);
          else if ((hidden = "msHidden") in document)
            document.addEventListener("msvisibilitychange", onchange);
          else if ("onfocusin" in document) {
                // IE 9 and lower:
            document.onfocusin = onshow;
                document.onfocusout = onhide;
          } else {
                // All others:
            window.onpageshow = window.onfocus = onshow;
                window.onpagehide = window.onblur = onhide;
            }

          function onchange (evt) {
                //occurs both on leaving and on returning
                currentlyVisible = !currentlyVisible;
                doSomethingIfAppropriate();
          }

            function onshow(evt) {
                //for older browsers
                currentlyVisible = true;
                doSomethingIfAppropriate();
            }

            function onhide(evt) {
                //for older browsers
                currentlyVisible = false;
                doSomethingIfAppropriate();
            }

            function doSomethingIfAppropriate() {
                if (currentlyVisible) {
                    //trigger angular digest cycle in this scope
                    scope.$apply(function() {
                        functionOrExpression(scope);
                    });
                }
            }
        }
    };

});

Vous pouvez l'utiliser comme cet exemple:, <div react-on-window-focus="refresh()">refresh()est une fonction de portée dans la portée de tout contrôleur qui est dans la portée.

Steve Campbell
la source
0

Voici une solution solide et moderne. (Court un doux 👌🏽)

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

Cela configurera un écouteur pour qu'il se déclenche lorsqu'un événement de visibilité est déclenché, ce qui pourrait être un focus ou un flou.

Cory Robinson
la source
0

Si vous souhaitez agir sur l' ensemble du flou du navigateur : comme je l'ai dit, si le navigateur perd le focus, aucun des événements suggérés ne se déclenche. Mon idée est de compter en boucle et de réinitialiser le compteur si un événement se déclenche. Si le compteur atteint une limite, je fais une location.href vers une autre page. Cela se déclenche également si vous travaillez sur des outils de développement.

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

Il s'agit d'un projet testé avec succès sur FF.

BF
la source