Détecter si l'onglet du navigateur a le focus


149

Existe-t-il un moyen fiable pour plusieurs navigateurs de détecter qu'un onglet a le focus?

Le scénario est que nous avons une application qui interroge régulièrement les cours des actions, et si la page n'a pas de focus, nous pourrions arrêter le sondage et éviter à tout le monde le bruit du trafic, d'autant plus que les gens sont fans d'ouvrir plusieurs onglets avec différents portefeuilles.

Est window.onbluret window.onfocusune option pour cela?


3
duplication possible de Existe
lucian

Réponses:


127

Oui, window.onfocuset window.onblurdevrait fonctionner pour votre scénario:

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


3
L'aspect onfocusin / onfocusout de ceci, ainsi que la note indiquant à l'utilisateur que vous avez fait une pause sont de très bonnes notes. Merci.
Fenton

7
Veuillez noter que vous ne pouvez pas faire la distinction entre la page active ou inactive lors du chargement de la page de cette façon.
pimvdb

@SteveFenton - onfocusest crossbrowser, où les événements que vous avez mentionnés sont uniquement IE, je ne vois pas pourquoi cela serait considéré comme une bonne note de votre part ..
vsync

1
@vsync - lisez l'article lié, vous verrez qu'il utilise à la fois «onfocusin» et «onfocus».
Fenton

Pourriez-vous au moins mentionner la différence entre les deux?
Lenar Hoyt

53

Modification importante: cette réponse est obsolète. Depuis son écriture, l'API Visibility ( mdn , exemple , spec ) a été introduite. C'est la meilleure façon de résoudre ce problème.


var focused = true;

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

AFAIK, focuset blursont tous pris en charge sur ... tout. (voir http://www.quirksmode.org/dom/events/index.html )


2
Juste une petite note, avec toutes ces solutions, vous courez le risque que l'utilisateur change d'onglet avant que le javascript ne soit complètement chargé, attribuant ainsi la mauvaise valeur à focus. Pas sûr qu'il existe un bon moyen de contourner cela.
JayD3e

Les liens de mise à jour sont exactement ce que je recherchais. Merci de les ajouter!
webLacky3rdClass

La question est spécifiquement de détecter si une page a le focus, ce qui est différent de détecter si la page est visible. Plusieurs pages peuvent être visibles en même temps (dans différentes fenêtres), tandis qu'une seule peut avoir le focus. Utilisez la technique qui convient à vos besoins, mais connaissez la différence.
jaredjacobs

1
Il s'agit d'une solution dangereuse car elle risque de remplacer un autre écouteur d'événement dans une application plus grande. Vous devriez plutôt suivre cette réponse: stackoverflow.com/a/21935031/549503
mmmeff

51

Lors de la recherche sur ce problème, j'ai trouvé une recommandation selon laquelle l' API de visibilité de page devrait être utilisée. La plupart des navigateurs modernes prennent en charge cette API selon Can I Use: http://caniuse.com/#feat=pagevisibility .

Voici un exemple de travail (dérivé de cet extrait de code ):

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

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

  var document_hidden = document[hidden];

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

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

Mise à jour: L'exemple ci-dessus avait l'habitude d'avoir des propriétés préfixées pour les navigateurs Gecko et WebKit, mais j'ai supprimé cette implémentation car ces navigateurs proposent depuis un certain temps une API de visibilité de page sans préfixe. J'ai gardé le préfixe spécifique de Microsoft afin de rester compatible avec IE10.


Lorsque les préfixes du fournisseur disparaissent, je vais probablement changer!
Fenton

Le seul vrai problème avec cela ne concerne pas les préfixes de fournisseur car il existe une recommandation officielle du W3C (datée du 29 octobre 2013). Le problème dans certains cas est que l'API de visibilité de page est prise en charge dans IE10 et les versions ultérieures. Si vous avez besoin de prendre en charge IE9, vous devriez rechercher une approche différente…
Ilija

C'est la bonne façon de le faire pour tous les navigateurs modernes. +1
Ajedi32

Êtes-vous sûr que ces préfixes de fournisseur sont même nécessaires? Selon MDN et CanIUse, ils n'ont pas été nécessaires sur Chrome depuis la version 32, ou sur Firefox depuis la version 17, et ils n'ont jamais été nécessaires sur IE.
Ajedi32

@ Ajedi32 Merci. Je vais devoir faire quelques tests et creuser pour voir ce qui reste pertinent et ce qui peut être laissé de côté maintenant.
Ilija

37

Surprenant de ne voir personne mentionné document.hasFocus

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

MDN a plus d'informations.


fonctionne pour moi (testé sur Chrome et Firefox). La réponse acceptée (onfocus / onblur) n'a pas fonctionné
Harmv

La bonne réponse encore une fois tout en bas. Bravo StackOverflow!
Onze octobre

vraiment, n'est-ce pas la réponse parfaite? quelqu'un voit-il un inconvénient?
gaspar

2
Le seul inconvénient est que si vous essayez de déterminer si l'onglet est mis au point à partir d'une iframe, cela échouerait si l'iframe était chargé alors que la page parente était toujours floue. Pour couvrir cela également, vous devrez utiliser l'API de visibilité de la page.
Ivan le

29

Oui, cela devrait fonctionner pour vous. Vous venez de me rappeler ce lien que j'ai rencontré qui exploite ces techniques. lecture intéressante


2
+1 - c'est une astuce très intelligente, je pourrais imaginer que tromper beaucoup de gens.
Fenton le

2
Quelle attaque ingénieuse et sournoise. Intéressant de lire ça, merci.
Voo le

4

Je le ferais de cette façon (Référence http://www.w3.org/TR/page-visibility/ ):

    window.onload = function() {

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

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


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

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

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

    };  

Pouvez-vous expliquer en quoi cette réponse diffère de la réponse donnée par @Ilija - il peut y avoir une différence, mais elle est subtile - alors une explication de ce que c'est et pourquoi elle devrait être différente serait appréciée.
Fenton

2

Solution jQuery de navigateur croisé! Raw disponible sur GitHub

Amusant et facile à utiliser!

Le plugin suivant passera par votre test standard pour différentes versions d'IE, Chrome, Firefox, Safari, etc. et établira vos méthodes déclarées en conséquence. Il traite également de problèmes tels que:

  • onblur | .blur / onfocus | .focus " dupliquer " les appels
  • fenêtre perdant le focus en sélectionnant une autre application, comme word
    • Cela a tendance à être indésirable simplement parce que, si vous avez une page bancaire ouverte, et que l' événement onblur lui dit de masquer la page, alors si vous ouvrez la calculatrice, vous ne pouvez plus voir la page!
  • Ne se déclenche pas lors du chargement de la page

L'utilisation est aussi simple que: Faites défiler jusqu'à « Exécuter l'extrait de code »

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

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

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

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

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

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


Vous devez mettre le code non minimisé pour le plugin.
Patrick Desjardins

@PatrickDesjardins ouais. Prévoyez de faire cela ce week-end avec d'autres choses. JE? Faites une idée de tout ce que j'ai. Jdmckinstry sur github. Ajoutera des liens vers d'anciennes réponses comme celles-ci au fur et à mesure que je les ajouterai à l'essentiel
SpYk3HH

Que faire si je souhaite que la page perde le focus, lorsque je passe à une autre application, telle que "Word" ou "Calculatrice"?
Benas

@Benas C'est peut-être faux, mais je pense que c'est la fonctionnalité de base du très basique jQuery(window).blur/focus, qui n'était pas souhaitée par beaucoup, donc l'une des raisons pour lesquelles j'ai créé ce plugin. Le plugin est destiné à aider à fournir ce que jQuery ne fait pas déjà
SpYk3HH
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.