web-dev-qa-db-fra.com

Détecter les popups bloqués dans Chrome

Je connais les techniques javascript permettant de détecter si un popup est bloqué dans d'autres navigateurs (comme décrit dans la réponse à cette question ). Voici le test de base:

var newWin = window.open(url);

if(!newWin || newWin.closed || typeof newWin.closed=='undefined')
{
    //POPUP BLOCKED
}

Mais cela ne fonctionne pas dans Chrome. La section "POPUP BLOCKED" n'est jamais atteinte lorsque la popup est bloquée.

Bien sûr, le test fonctionne dans une certaine mesure puisque Chrome ne bloque pas le popup, mais l’ouvre dans une petite fenêtre minimisée dans le coin inférieur droit qui répertorie les popups "bloqués".

Ce que je voudrais faire, c'est pouvoir dire si le popup a été bloqué par le bloqueur de popup de Chrome. J'essaie d'éviter que le navigateur renifle en faveur de la détection de fonctionnalités. Y a-t-il un moyen de faire cela sans sniffer le navigateur?

Edit : J'ai maintenant essayé d'utiliser newWin.outerHeight, newWin.left, et d'autres propriétés similaires pour accomplir ceci. Google Chrome renvoie toutes les valeurs de position et de hauteur en tant que 0 lorsque la fenêtre contextuelle est bloquée.

Malheureusement, il renvoie également les mêmes valeurs même si la fenêtre contextuelle est réellement ouverte pour une durée indéterminée. Après quelques instants magiques (quelques secondes lors de mes tests), les informations d'emplacement et de taille sont renvoyées sous forme de valeurs correctes. En d'autres termes, je ne suis toujours pas près de comprendre cela. Toute aide serait appréciée.

100
Andrew Ensley

Eh bien, le "temps magique" dont vous parlez est probablement lorsque le DOM de la popup a été chargé. Ou alors cela pourrait être quand tout (images, CSS externe, etc.) a été chargé. Vous pouvez le tester facilement en ajoutant un très grand graphique à la fenêtre contextuelle (effacez d'abord votre cache!). Si vous utilisiez un framework Javascript tel que jQuery (ou quelque chose de similaire), vous pouvez utiliser l'événement ready () (ou quelque chose de similaire) pour attendre le chargement du DOM avant de vérifier le décalage de la fenêtre. Le danger en cela est que la détection Safari fonctionne de manière contradictoire: le DOM de la fenêtre contextuelle ne sera jamais prêt () dans Safari, car il vous donnera un identifiant valide pour la fenêtre que vous essayez d’ouvrir - qu’elle s’ouvre ou non. ne pas. (En fait, je crois que votre code de test contextuel ci-dessus ne fonctionnera pas pour le safari.)

Je pense que la meilleure chose à faire est d’envelopper votre test dans un setTimeout () et de laisser au popup 3 à 5 secondes pour terminer le chargement avant d’exécuter le test. Ce n'est pas parfait, mais cela devrait fonctionner au moins 95% du temps.

Voici le code que j'utilise pour la détection inter-navigateurs, sans la partie Chrome.

function _hasPopupBlocker(poppedWindow) {
    var result = false;

    try {
        if (typeof poppedWindow == 'undefined') {
            // Safari with popup blocker... leaves the popup window handle undefined
            result = true;
        }
        else if (poppedWindow && poppedWindow.closed) {
            // This happens if the user opens and closes the client window...
            // Confusing because the handle is still available, but it's in a "closed" state.
            // We're not saying that the window is not being blocked, we're just saying
            // that the window has been closed before the test could be run.
            result = false;
        }
        else if (poppedWindow && poppedWindow.test) {
            // This is the actual test. The client window should be fine.
            result = false;
        }
        else {
            // Else we'll assume the window is not OK
            result = true;
        }

    } catch (err) {
        //if (console) {
        //    console.warn("Could not access popup window", err);
        //}
    }

    return result;
}

Ce que je fais est d'exécuter ce test à partir du parent et de l'envelopper dans un setTimeout (), en laissant à la fenêtre enfant 3-5 secondes pour se charger. Dans la fenêtre enfant, vous devez ajouter une fonction de test:

test de fonctionnalité() {}

Le détecteur de bloqueur de popup vérifie si la fonction "test" existe en tant que membre de la fenêtre enfant.

AJOUTÉ LE 15 JUIN 2015:

Je pense que la manière moderne de gérer cela serait d’utiliser window.postMessage () pour que l’enfant informe le parent que la fenêtre a été chargée. L'approche est similaire (l'enfant dit au parent qu'elle est chargée), mais les moyens de communication se sont améliorés. J'ai pu faire ce multi-domaine de l'enfant:

$(window).load(function() {
  this.opener.postMessage({'loaded': true}, "*");
  this.close();
});

Le parent écoute ce message en utilisant:

$(window).on('message', function(event) {     
  alert(event.originalEvent.data.loaded)
}); 

J'espère que cela t'aides.

63
Ringo

Juste une amélioration au snipet d'InvisibleBacon (testé dans IE9, Safari 5, Chrome 9 et FF 3.6)):

var myPopup = window.open("popupcheck.htm", "", "directories=no,height=150,width=150,menubar=no,resizable=no,scrollbars=no,status=no,titlebar=no,top=0,location=no");
if (!myPopup)
    alert("failed for most browsers");
else {
    myPopup.onload = function() {
        setTimeout(function() {
            if (myPopup.screenX === 0) {
                alert("failed for chrome");
            } else {
                // close the test window if popups are allowed.
                myPopup.close();  
            }
        }, 0);
    };
}
15
Andy

Voici une solution jQuery à la vérification du bloqueur de fenêtres contextuelles. Il a été testé dans les versions FF (v11), Safari (v6), Chrome (v23.0.127.95)) & IE (v7 & v9). la fonction _displayError pour gérer le message d'erreur comme bon vous semble.

var popupBlockerChecker = {
        check: function(popup_window){
            var _scope = this;
            if (popup_window) {
                if(/chrome/.test(navigator.userAgent.toLowerCase())){
                    setTimeout(function () {
                        _scope._is_popup_blocked(_scope, popup_window);
                     },200);
                }else{
                    popup_window.onload = function () {
                        _scope._is_popup_blocked(_scope, popup_window);
                    };
                }
            }else{
                _scope._displayError();
            }
        },
        _is_popup_blocked: function(scope, popup_window){
            if ((popup_window.innerHeight > 0)==false){ scope._displayError(); }
        },
        _displayError: function(){
            alert("Popup Blocker is enabled! Please add this site to your exception list.");
        }
    };

Usage:

var popup = window.open("http://www.google.ca", '_blank');
popupBlockerChecker.check(popup);

J'espère que cela t'aides! :)

12
Kevin B

La réponse de Rich ne fonctionnera plus pour Chrome. On dirait que Chrome exécute le Javascript présentement dans la fenêtre contextuelle. J'ai fini par vérifier si la valeur screenX était égale à 0 afin de détecter les fenêtres bloquées. Je pense également avoir trouvé un moyen de garantir que cette propriété est final avant de vérifier Cela ne fonctionne que pour les popups sur votre domaine, mais vous pouvez ajouter un gestionnaire de chargement comme ceci:

var myPopup = window.open("site-on-my-domain", "screenX=100");
if (!myPopup)
    alert("failed for most browsers");
else {
    myPopup.onload = function() {
        setTimeout(function() {
            if (myPopup.screenX === 0)
                alert("failed for chrome");
        }, 0);
    };
}

Comme beaucoup l'ont signalé, la propriété "screenX" signale parfois des valeurs non nulles pour les popups ayant échoué, même après une surcharge. J'ai également rencontré ce problème, mais si vous ajoutez la vérification après un délai d'expiration de zéro ms, la propriété screenX semble toujours générer une valeur cohérente.

Faites-moi savoir s'il existe des moyens de rendre ce script plus robuste. Semble fonctionner pour mes buts cependant.

10
InvisibleBacon

Cela a fonctionné pour moi:

    cope.PopupTest.params = 'height=1,width=1,left=-100,top=-100,location=no,toolbar=no,menubar=no,scrollbars=no,resizable=no,directories=no,status=no';
    cope.PopupTest.testWindow = window.open("popupTest.htm", "popupTest", cope.PopupTest.params);

    if( !cope.PopupTest.testWindow
        || cope.PopupTest.testWindow.closed
        || (typeof cope.PopupTest.testWindow.closed=='undefined')
        || cope.PopupTest.testWindow.outerHeight == 0
        || cope.PopupTest.testWindow.outerWidth == 0
        ) {
        // pop-ups ARE blocked
        document.location.href = 'popupsBlocked.htm';
    }
    else {
        // pop-ups are NOT blocked
        cope.PopupTest.testWindow.close();
    }

OuterHeight et outerWidth sont pour chrome car l'astuce "about: blank" ci-dessus ne fonctionne plus dans chrome.

9

Je vais simplement copier/coller la réponse fournie ici: https://stackoverflow.com/a/27725432/892099 de DanielB. fonctionne sur chrome 40 et il est très propre. pas de piratage sale ni d’attente.

function popup(urlToOpen) {
  var popup_window=window.open(urlToOpen,"myWindow","toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=yes, resizable=yes, copyhistory=yes, width=400, height=400");            
  try {
    popup_window.focus();   
  }
  catch (e) {
    alert("Pop-up Blocker is enabled! Please add this site to your exception list.");
  }
}
4
Hamy

Wow, il y a certainement beaucoup de solutions ici. Ceci est le mien, il utilise des solutions tirées du réponse acceptée actuelle (qui ne fonctionne pas dans la dernière Chrome et nécessite de l'envelopper dans un délai d'attente)), ainsi que une solution connexe sur ce fil (qui est en fait Vanilla JS, pas jQuery).

Mine utilise une architecture de rappel qui sera envoyée true lorsque le popup sera bloqué et false sinon.

window.isPopupBlocked = function(popup_window, cb)
{
    var CHROME_CHECK_TIME = 2000;       // the only way to detect this in Chrome is to wait a bit and see if the window is present

    function _is_popup_blocked(popup)
    {
        return !popup.innerHeight;
    }

    if (popup_window) {
        if (popup_window.closed) {
            // opened OK but was closed before we checked
            cb(false);
            return;
        }
        if (/chrome/.test(navigator.userAgent.toLowerCase())) {
            // wait a bit before testing the popup in chrome
            setTimeout(function() {
                cb(_is_popup_blocked(popup_window));
            }, CHROME_CHECK_TIME);
        } else {
            // for other browsers, add an onload event and check after that
            popup_window.onload = function() {
                cb(_is_popup_blocked(popup_window));
            };
        }
    } else {
        cb(true);
    }
};
2
pospi

Que diriez-vous d'une approche Promise?

const openPopUp = (...args) => new Promise(s => {
  const win = window.open(...args)
  if (!win || win.closed) return s()
  setTimeout(() => (win.innerHeight > 0 && !win.closed) ? s(win) : s(), 200)
})

Et vous pouvez l'utiliser comme le classique window.open

const win = await openPopUp('popuptest.htm', 'popuptest')
if (!win) {
  // popup closed or blocked, handle alternative case
}

Vous pouvez changer le code pour qu'il ne respecte pas la promesse au lieu de retourner undefined, je pensais juste que if était un flux de contrôle plus facile que try / catch pour ce cas.

2
kigiri

Voici une version qui fonctionne actuellement dans Chrome. Juste une petite modification de la solution de Rich, bien que j'ai ajouté un wrapper qui gère aussi le timing.

function checkPopupBlocked(poppedWindow) {
 setTimeout(function(){doCheckPopupBlocked(poppedWindow);}, 5000);
}

function doCheckPopupBlocked(poppedWindow) {

    var result = false;

    try {
        if (typeof poppedWindow == 'undefined') {
            // Safari with popup blocker... leaves the popup window handle undefined
            result = true;
        }
        else if (poppedWindow && poppedWindow.closed) {
            // This happens if the user opens and closes the client window...
            // Confusing because the handle is still available, but it's in a "closed" state.
            // We're not saying that the window is not being blocked, we're just saying
            // that the window has been closed before the test could be run.
            result = false;
        }
        else if (poppedWindow && poppedWindow.outerWidth == 0) {
            // This is usually Chrome's doing. The outerWidth (and most other size/location info)
         // will be left at 0, EVEN THOUGH the contents of the popup will exist (including the
         // test function we check for next). The outerWidth starts as 0, so a sufficient delay
         // after attempting to pop is needed.
            result = true;
        }
        else if (poppedWindow && poppedWindow.test) {
            // This is the actual test. The client window should be fine.
            result = false;
        }
        else {
            // Else we'll assume the window is not OK
            result = true;
        }

    } catch (err) {
        //if (console) {
        //    console.warn("Could not access popup window", err);
        //}
    }

    if(result)
     alert("The popup was blocked. You must allow popups to use this site.");
}

Pour l'utiliser, il suffit de faire ceci:

var popup=window.open('location',etc...);
checkPopupBlocked(popup);

Si la fenêtre contextuelle est bloquée, le message d'alerte s'affichera après la période de grâce de 5 secondes (vous pouvez régler cela, mais 5 secondes devraient être relativement sûres).

2
Kandelon

Vérifiez la position de la fenêtre par rapport au parent. Chrome fait apparaître la fenêtre presque hors écran.

2
Jason Cohen

Ce fragment incorpore tout ce qui précède - Pour une raison quelconque - StackOverflow exclut la première et la dernière ligne de code dans le bloc de code ci-dessous, j'ai donc écrit un blog à ce sujet. Pour une explication complète et le reste du code (téléchargeable), jetez un œil à mon blog sur thecodeabode.blogspot.com

var PopupWarning = {

    init : function()
    {

        if(this.popups_are_disabled() == true)
        {
            this.redirect_to_instruction_page();
        }
    },

    redirect_to_instruction_page : function()
    {
        document.location.href = "http://thecodeabode.blogspot.com";
    },

    popups_are_disabled : function()
    {
        var popup = window.open("http://localhost/popup_with_chrome_js.html", "popup_tester", "width=1,height=1,left=0,top=0");

        if(!popup || popup.closed || typeof popup == 'undefined' || typeof popup.closed=='undefined')
        {
            return true;
        }

        window.focus();
        popup.blur();

        //
        // Chrome popup detection requires that the popup validates itself - so we need to give
        // the popup time to load, then call js on the popup itself
        //
        if(navigator && (navigator.userAgent.toLowerCase()).indexOf("chrome") > -1)
        {
            var on_load_test = function(){PopupWarning.test_chrome_popups(popup);};     
            var timer = setTimeout(on_load_test, 60);
            return;
        }


        popup.close();
        return false;
    },

    test_chrome_popups : function(popup)
    {
        if(popup && popup.chrome_popups_permitted && popup.chrome_popups_permitted() == true)
        {
            popup.close();
            return true;
        }

        //
        // If the popup js fails - popups are blocked
        //
        this.redirect_to_instruction_page();
    }
};

PopupWarning.init();
2
Ben

J'ai eu un problème similaire avec les popups qui ne s'ouvraient pas dans Chrome. J'étais frustré parce que je n'essayais pas de faire quelque chose de sournois, comme une fenêtre contextuelle de chargement, ouvrant simplement une fenêtre lorsque l'utilisateur cliquait. J'étais DOUBLEMENT frustré parce qu'exécuter ma fonction qui incluait window.open () à partir de la ligne de commande firebug fonctionnait, alors que cliquer sur mon lien ne fonctionnait pas! Voici ma solution:

Mauvaise façon: exécuter window.open () à partir d'un écouteur d'événement (dans mon cas, dojo.connect à la méthode d'événement onclick d'un nœud DOM).

dojo.connect(myNode, "onclick", function() {
    window.open();
}

Droit chemin: attribuer une fonction à la propriété onclick du nœud appelé window.open ().

myNode.onclick = function() {
    window.open();
}

Et, bien sûr, je peux toujours faire des écouteurs d'événement pour le même événement onclick si j'en ai besoin. Avec ce changement, je pouvais ouvrir mes fenêtres même si Chrome était réglé sur "Ne permettre à aucun site d’afficher des fenêtres contextuelles". Joy.

Si quelqu'un de sage à la manière de Chrome peut dire au reste de nous pourquoi cela fait une différence, j'aimerais l'entendre, bien que je soupçonne qu'il s'agisse simplement d'une tentative de fermer la porte aux réseaux malveillants. popups programmatiques.

2
Chris

La réponse de Jason est la seule méthode à laquelle je puisse penser aussi, mais compter sur une telle position est un peu risqué!

De nos jours, vous n'avez pas vraiment besoin de poser la question "mon popup a-t-il été bloqué?", Car la réponse est invariablement "oui" - le bloqueur de popup est activé par défaut sur tous les principaux navigateurs. La meilleure approche consiste uniquement à window.open () en réponse à un clic direct, ce qui est presque toujours autorisé.

1
bobince
function openPopUpWindow(format)
{   
    var win = window.open('popupShow.html',
                          'ReportViewer',
                          'width=920px,height=720px,left=50px,top=20px,location=no,directories=no,status=no,menubar=no,toolbar=no,resizable=1,maximize:yes,scrollbars=0');

    if (win == null || typeof(win) == "undefined" || (win == null && win.outerWidth == 0) || (win != null && win.outerHeight == 0) || win.test == "undefined") 
    {
        alert("The popup was blocked. You must allow popups to use this site.");  
    }
    else if (win)
    {
        win.onload = function()
        {          
            if (win.screenX === 0) {
                alert("The popup was blocked. You must allow popups to use this site.");
                win.close();
            } 
        };
    }
}
1
syed

SALUT

J'ai légèrement modifié les solutions décrites ci-dessus et je pense que cela fonctionne au moins pour Chrome. Ma solution est faite pour détecter si le popup est bloqué quand la page principale est ouverte, pas quand le popup est ouvert, mais je suis sûr qu'il y a des gens qui peuvent le modifier. :-) L'inconvénient ici est que la fenêtre popup est affichée pendant quelques secondes (peut être possible de raccourcir un peu) quand il n'y a pas de popup-bloquant.

Je mets ceci dans la section de ma fenêtre principale

<script type="text/JavaScript" language="JavaScript">

 var mine = window.open('popuptest.htm','popuptest','width=1px,height=1px,left=0,top=0,scrollbars=no');
 if(!mine|| mine.closed || typeof mine.closed=='undefined')
  {
    popUpsBlocked = true       
    alert('Popup blocker detected ');
    if(mine)
      mine.close();
 }
 else
 {
    popUpsBlocked = false    
    var cookieCheckTimer = null;
    cookieCheckTimer =  setTimeout('testPopup();', 3500);
 }


function testPopup()
{
  if(mine)
  {
    if(mine.test())
    {
       popUpsBlocked = false;
    }
    else
    {
        alert('Popup blocker detected ');
         popUpsBlocked = true;
     }
    mine.close();
}

} 
</script>

Le popuptest ressemble à ceci:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
    <title>Popup test</title>
<script type="text/javascript" language="Javascript">
   function test() {if(window.innerHeight!=0){return true;} else return false;}
</script>
</head>

<body>
</body>
</html>

Comme j'appelle la fonction de test sur la page contextuelle après 3500 ms, la hauteur intérieure a été correctement définie par Chrome.

J'utilise la variable popUpsBlocked pour savoir si les popups sont affichés ou non dans d'autres javascripts. c'est à dire

function ShowConfirmationMessage()
{
if(popUpsBlocked)
 { 
  alert('Popups are blocked, can not display confirmation popup. A mail will be sent with the confirmation.');
 } 
 else
 { 
  displayConfirmationPopup();
 }
 mailConfirmation();
}
1
Lars

Autant que je sache (d'après ce que j'ai testé) Chrome renvoie un objet window avec l'emplacement de "about: blank". Les éléments suivants devraient donc fonctionner pour tous les navigateurs:

var newWin = window.open(url);
if(!newWin || newWin.closed || typeof newWin.closed=='undefined' || newWin.location=='about:blank')
{
    //POPUP BLOCKED
}
0
Yoav Aharoni