Évitez les bloqueurs de fenêtres contextuelles du navigateur

172

Je développe un flux d'authentification OAuth purement en JavaScript et je souhaite montrer à l'utilisateur la fenêtre "Accorder l'accès" dans une fenêtre contextuelle, mais elle est bloquée.

Comment puis-je empêcher les fenêtres pop-up créées par l'un window.openou l' autre ou window.showModalDialogd'être bloquées par les bloqueurs de fenêtres pop-up des différents navigateurs?

Pablo Fernandez
la source
11
Même si c'était possible (je ne sais pas), si les gens utilisent des bloqueurs de popup, vous devriez le respecter. La plupart des navigateurs affichent un message lorsqu'un site tente d'ouvrir une fenêtre contextuelle afin qu'ils puissent toujours le voir s'ils le souhaitent. Vous pouvez mettre une remarque sur votre site selon laquelle certains contenus s'ouvre dans une fenêtre contextuelle et que l'utilisateur doit l'autoriser pour continuer.
Felix Kling
5
La meilleure pratique devrait ressembler à: 1) Faites-le avec succès 2) Fermez vos fenêtres et barrez votre porte et recroquevillé de peur de la foule croissante de mécènes Web contrariés 3) Repentez-vous, supprimez le pop-up-busta-busta et respectez votre public.
Alex Mcp
Alex et Felix, j'ai mis à jour la question. Je n'utiliserai pas la connaissance pour le mal :). Merci!
Pablo Fernandez
9
Je voudrais ajouter que contourner un bloqueur de fenêtres contextuelles peut en fait essayer d'améliorer l'expérience utilisateur. Dans un exemple sur lequel je travaille actuellement, nous utilisons une application Javascript (basée sur ExtJS) et nous essayons de laisser les utilisateurs payer en utilisant paypal. Nous leur donnons un bouton sur lequel ils peuvent cliquer pour lancer paypal dans une nouvelle fenêtre, mais certaines versions d'IE le bloquent en tant que popup (même s'il s'agit d'un clic sur un bouton). S'ils activent maintenant le popup, l'écran se recharge et en tant qu'application JavaScript, nous perdons l'état de la fenêtre et ils doivent recommencer. Alors vraiment: le problème est que IE est stupide.
NateDSaint
1
@FelixKling Oui, c'est possible. Les fabricants de navigateurs y ont déjà pensé. L'ouverture d'une fenêtre contextuelle est OK dans la mesure où il y a une intention de l'utilisateur (signalée par l'utilisateur cliquant sur un lien ou un bouton). Les bloqueurs de fenêtres publicitaires doivent respecter l'intention de l'utilisateur. Si le bloqueur de popup d'IE ne le fait pas, c'est le bloqueur de popup qui est en faute. Les utilisateurs utilisent des bloqueurs de popup pour empêcher les scripts d'ouvrir des popups à volonté, pas pour bloquer les popups qu'ils ont eux-mêmes essayé d'ouvrir (en cliquant sur un bouton ou un lien).
Stijn de Witt

Réponses:

286

La règle générale est que les bloqueurs de popups s'engageront si window.openou similaire est invoqué à partir de javascript qui n'est pas invoqué par une action directe de l'utilisateur . Autrement dit, vous pouvez appeler window.openen réponse à un clic sur un bouton sans être touché par le bloqueur de fenêtres contextuelles, mais si vous mettez le même code dans un événement de minuterie, il sera bloqué. La profondeur de la chaîne d'appels est également un facteur - certains navigateurs plus anciens ne regardent que l'appelant immédiat, les nouveaux navigateurs peuvent revenir un peu en arrière pour voir si l'appelant de l'appelant était un clic de souris, etc.

dthorpe
la source
2
Fait intéressant, les fenêtres contextuelles initiées via un événement de modification lié à un élément sélectionné seront bloquées (dans Chrome, pas dans FF), même si cet événement est déclenché par une action directe de l'utilisateur, comme un clic. Bien que liés à une entrée, ils sont autorisés. Étrange.
Ccnokes le
6
Personne n'a dit que le navigateur était cohérent. : P
dthorpe
8
Grâce à des expériences, je dois comprendre que la profondeur de la pile n'a rien à voir avec le bloqueur de fenêtres contextuelles. Il vérifie en fait si window.open est appelé dans la seconde qui suit l'action de l'utilisateur ou non. Testé dans Chrome 46 et Firefox 42.
Mesqalito
Le délai de 1 seconde peut être contourné dans les deux navigateurs, ce qui permet une durée indéfinie en utilisant la réponse de @ tj-crowder sur cette page . ) pendant un certain temps avant de renvoyer une réponse. Le navigateur se bloque pendant qu'il «charge» la page .. puis déclenche le popup lorsqu'il reçoit une réponse. Dans Chrome, cependant, vous devez ajouter une alerte () juste avant l'ajax () pour que cette astuce fonctionne.
fanfare du
184

Sur la base de Jason Sebring est astuce très utile , et sur les choses couvertes ici et , je trouve une solution parfaite pour mon cas:

Pseudo code avec des extraits de code Javascript:

  1. créer immédiatement une fenêtre contextuelle vide sur l'action de l'utilisateur

    var importantStuff = window.open('', '_blank');

    Facultatif: ajoutez un message d'information "en attente". Exemples:

    a) Une page HTML externe: remplacez la ligne ci-dessus par

    var importantStuff = window.open('http://example.com/waiting.html', '_blank');

    b) Texte: ajoutez la ligne suivante sous celle ci-dessus:

    importantStuff.document.write('Loading preview...');
  2. le remplir de contenu lorsque vous êtes prêt (lorsque l'appel AJAX est renvoyé, par exemple)

    importantStuff.location.href = 'http://shrib.com';

Enrichissez l'appel à window.open avec toutes les options supplémentaires dont vous avez besoin.

J'utilise en fait cette solution pour une redirection mailto, et cela fonctionne sur tous mes navigateurs (windows 7, Android). le_blank bit permet à la redirection mailto de fonctionner sur mobile, btw.

Ton expérience? Un moyen d'améliorer cela?

Monsieur suisse
la source
1
? alors que faites-vous si ce n'est pas une action de l'utilisateur dans le navigateur? Pour mon exemple, une fois que l'utilisateur s'est authentifié
Don Cheadle
@mmcrae ne fait pas ça. Quoi que vous ayez l'intention de faire, il existe un meilleur moyen qu'une fenêtre contextuelle. Sinon, il y a de fortes chances que je ne veuille jamais voir votre site. Les navigateurs actuels s'assurent que vous ne devriez pas pouvoir le faire - voir la réponse de @dthorpe .
Monsieur suisse
5
Whatever it is you intend to do, there is a better way than a popup windowlol? Généralisation étrange. Le client souhaite que la page sur laquelle il s'est authentifié reste ouverte et que le nouveau site / page de destination après l'authentification s'ouvre dans un nouvel onglet. Oui, ce n'est pas mon idée d'une excellente expérience utilisateur ... mais cela semble raisonnable
Don Cheadle
@mmcrae. Asseyez-vous et réfléchissez sérieusement. Je vous promets que vous trouverez une "action utilisateur" dans le scénario que vous décrivez. Le point entier de ma réponse est que vous créez la fenêtre contextuelle lorsque vous avez l'action de l'utilisateur - puis la remplissez de contenu plus tard. Astuce pour votre cas: l'utilisateur frappe "authentifier" -> créer le popup (vide); le minuteur est activé ou la réponse du serveur est de retour ou autre -> remplissez le contenu dans la fenêtre contextuelle.
Monsieur suisse
3
Conseil utile, si la requête ajax échoue, appelez importantStuff.closepour fermer le nouvel onglet et fournir une alerte dans la page d'origine.
Goose
24

De plus Swiss Mister post, dans mon cas, le window.open a été lancé à l'intérieur d'une promesse, ce qui a activé le bloqueur de popup, ma solution était: en angulaire:

$scope.gotClick = function(){

  var myNewTab = browserService.openNewTab();
  someService.getUrl().then(
    function(res){
        browserService.updateTabLocation(res.url, myNewTab);

    }
  );
};

browserService:

this.openNewTab = function(){
     var newTabWindow = $window.open();
     return newTabWindow;
}

this.updateTabLocation = function(tabLocation, tab) {
     if(!tabLocation){
       tab.close();
     }
     tab.location.href = tabLocation;
}

c'est ainsi que vous pouvez ouvrir un nouvel onglet en utilisant la réponse de promesse et sans invoquer le bloqueur de popup.

David
la source
1
Cela a conduit à ma solution! Où j'ai créé une variable contenant l'onglet ouvert, puis rempli l'URL après le chargement des données. Merci. const tab = window.open(); observable.subscribe(dataUrl => tab.location.href = dataUrl);
jonas
1
Si vous avez activé le bloqueur de popup, cela ne résoudra pas le problème avec le bloqueur de popup ...
Alejandro Vales
La solution @Alejandro Vales jonas ne fonctionnera que si le code s'exécutera à partir d'un événement onClick ou de toute interaction de l'utilisateur. Lorsque vous essayez d'ouvrir un nouvel onglet via windows.open à l'intérieur d'une promesse, timeout, subscribe ... le navigateur pense que c'est une manipulation sur l'utilisateur, donc la solution est de créer une instance avant d'entrer la promesse, à l'intérieur il vous suffit de changer le href comme jonas l'a fait
David
@David Merci beaucoup !!! Je n'ai pas vu la .thenréponse sur alors et c'est pourquoi je suis si confus: / SRY ...
Alejandro Vales
@David C'est génial! Fonctionne comme un charme. Si je pouvais vous déranger avec juste une autre question - savez-vous comment faire fonctionner cela dans Edge également? Un coup de pouce vers une bonne ressource aiderait grandement ...
dzenesiz
21

En tant que bonne pratique, je pense que c'est une bonne idée de tester si une fenêtre contextuelle a été bloquée et de prendre des mesures au cas où. Vous devez savoir que window.open a une valeur de retour et que cette valeur peut être nulle si l'action échoue. Par exemple, dans le code suivant:

function pop(url,w,h) {
    n=window.open(url,'_blank','toolbar=0,location=0,directories=0,status=1,menubar=0,titlebar=0,scrollbars=1,resizable=1,width='+w+',height='+h);
    if(n==null) {
        return true;
    }
    return false;
}

si le popup est bloqué, window.open renverra null. La fonction retournera donc false.

A titre d'exemple, imaginez appeler cette fonction directement depuis n'importe quel lien avec target="_blank": si le popup est ouvert avec succès, le retour falsebloquera l'action du lien, sinon si le popup est bloqué, le retour truelaissera le comportement par défaut (ouvrir une nouvelle fenêtre _blank) et continuer .

<a href="http://whatever.com" target="_blank" onclick='return pop("http://whatever.com",300,200);' >

De cette façon, vous aurez une fenêtre contextuelle si cela fonctionne, et une fenêtre _blank sinon.

Si la fenêtre contextuelle ne s'ouvre pas, vous pouvez:

  • ouvrez une fenêtre vide comme dans l'exemple et continuez
  • ouvrir une fausse popup (une iframe à l'intérieur de la page)
  • informer l'utilisateur ("veuillez autoriser les popups pour ce site")
  • ouvrez une fenêtre vide puis informez l'utilisateur etc.
FrancescoMM
la source
Je vous remercie. Travaillé!
Bcktr
8

de l'API JavaScript oauth de Google:

http://code.google.com/p/google-api-javascript-client/wiki/Authentication

Voir la zone où il se lit:

Configuration de l'authentification

L'implémentation d'OAuth 2.0 par le client utilise une fenêtre contextuelle pour inviter l'utilisateur à se connecter et à approuver l'application. Le premier appel à gapi.auth.authorize peut déclencher des bloqueurs de popup, car il ouvre indirectement la fenêtre popup. Pour empêcher le bloqueur de popup de se déclencher sur les appels d'authentification, appelez gapi.auth.init (rappel) lorsque le client se charge. Le rappel fourni sera exécuté lorsque la bibliothèque sera prête à effectuer des appels d'authentification.

Je suppose que c'est lié à la vraie réponse ci-dessus dans la façon dont il explique s'il y a une réponse immédiate, il ne déclenchera pas l'alarme contextuelle. Le "gapi.auth.init" fait en sorte que l'API se produise immédiatement.

Application pratique

J'ai créé un microservice d'authentification open source en utilisant le passeport de nœud sur npm et les différents packages de passeport pour chaque fournisseur. J'ai utilisé une approche de redirection standard vers le tiers en lui donnant une URL de redirection vers laquelle revenir. C'était programmatique, donc je pouvais avoir différents endroits vers lesquels rediriger en cas de connexion / inscription et sur des pages particulières.

github.com/sebringj/athu

passeportjs.org

Jason Sebring
la source
3

J'ai essayé plusieurs solutions, mais c'est la seule qui a fonctionné pour moi dans tous les navigateurs

let newTab = window.open(); newTab.location.href = url;

pomobc
la source
2
Cela ne fonctionne tout simplement pas pour les personnes qui ont activé le bloqueur de popup
Alejandro Vales
qu'est ce que c'est url? il n'est pas attribué.
shinriyo
@shinriyo url est le lien vers la page à laquelle vous souhaitez accéder, par exemplehttp://example.com
pomobc
@AlejandroVales j'ai activé le bloqueur de popup et cela fonctionne. Le problème est qu'il window.open()ne peut pas ouvrir une nouvelle fenêtre par programme et si nous en avons besoin, cette réponse est l'une des options. Avec la newTabvariable à laquelle nous faisons référence window.open()et plus tard, nous pouvons l'appeler, insérer l' urlURL dont nous avons besoin, dans mon cas, l'url d'un objet blob, et le nouvel onglet sera ouvert avec l'url saisie.
stanimirsp
0

Je ne voulais pas créer la nouvelle page à moins que le rappel ne soit retourné avec succès, j'ai donc fait ceci pour simuler le clic de l'utilisateur:

function submitAndRedirect {
  apiCall.then(({ redirect }) => {
      const a = document.createElement('a');
      a.href = redirect;
      a.target = '_blank';
      document.body.appendChild(a);
      a.click();
      document.body.removeChild(a);
  });
}
user3479425
la source
3
ne fonctionne pas pour moi dans Chrome 62. Popup (page) est bloqué.
s.ermakovich
1
Oui, vous avez raison - j'ai également trouvé cela incohérent. J'ai fini par rendre mon appel api synchrone
user3479425
-8

Le moyen le plus simple de s'en débarrasser est de:

  1. N'utilisez pas document.open ().
  2. Utilisez plutôt this.document.location.href = location; où location est l'url à charger

Ex:

<script>
function loadUrl(location)
{
this.document.location.href = location;
}</script>

<div onclick="loadUrl('company_page.jsp')">Abc</div>

Cela a très bien fonctionné pour moi. À votre santé

Pramod Shetty
la source
2
Qu'en est-il d'ouvrir l'URL dans un nouvel onglet?
3 règles du