Contourner le bloqueur de popup sur window.open quand JQuery event.preventDefault () est défini

98

Je souhaite afficher une boîte de dialogue JQuery sous condition lors d'un événement de clic d'un lien hypertexte.

J'ai une exigence comme à condition1 d'ouvrir un dialogue JQuery et si la condition1 n'est pas satisfaite, accédez à la page référencée par la balise 'href' dont l'événement de clic est en question.

Je peux appeler une fonction sur l'événement de clic du lien. Cette fonction vérifie maintenant ladite condition en exécutant une autre URL (qui exécute mon contrôleur Spring et renvoie la réponse).

Tout fonctionne parfaitement avec seulement window.open étant bloqué par le bloqueur de popup.

$('a[href*=/viewpage?number]').live('click', function(e) {
    e.preventDefault();
    redirectionURL = this.href;
    pageId= getUrlVars(redirectionURL)["number"];
    $.getJSON("redirect/" + pageId, {}, function(status) {
        if (status == null) {
            alert("Error in verifying the status.");
        } else if(!status) {
            $("#agreement").dialog("open");
        } else {
            window.open(redirectionURL);
        }
    });
});

Si je supprime e.preventDefault();du code, le bloqueur de popoup ne bloque pas la page, mais pour la condition1, il ouvre alors le dialogue et ouvre la page 'href'.

Si j'en résous un, cela crée un problème pour un autre. Je ne suis pas en mesure de rendre justice aux deux conditions simultanément.

Pouvez-vous m'aider à résoudre ce problème s'il vous plaît?

Une fois que cela est résolu, j'ai un autre problème à résoudre, à savoir la navigation sur l'événement OK du dialogue :)

labyrinthe
la source
1
essayez de ne pas utiliser .live est obsolète et un pointeur vers .on ()!
mas-designs
@EvilP: Comme d'autres vous l'ont dit la dernière fois, seulement dans la version 1.7 et plus. Un grand nombre de projets sera encore sur 1,5 ou 1,6.
TJ Crowder
10
Downvoter: Ce n'est pas parce que vous n'aimez pas les popups que cette question doit être rejetée. Une question doit être rejetée si elle "... ne montre aucun effort de recherche; si elle n'est pas claire ou n'est pas utile" . La question est claire, ne semble pas paresseuse et découle d'une combinaison non triviale de facteurs.
TJ Crowder
@TJCrowder: Il n'a pas précisé la version qu'il utilise. Je devrais donc considérer qu'il utilise la dernière version. S'il utilise une version différente, je suis sûr qu'il le mentionnera car ALORS il serait conscient du fait que live est obsolète et expliquerait POURQUOI il utilise .live (). Personne ne m'a jamais dit la «dernière» fois, alors je pense que vous devriez faire une pause et vous calmer. Il n'y a pas besoin d'être si dur ...
mas-designs
Je n'ai reçu aucune notification à ce sujet. Mais n'est-il pas normal de considérer que si quelqu'un ne spécifie pas sa version pour considérer qu'il utilise la dernière? Je veux dire que je dois utiliser 1.3 pour une raison et je suis conscient du fait qu'il existe une version plus récente et meilleure.
mas-designs

Réponses:

144

Les bloqueurs de fenêtres publicitaires ne sont généralement autorisés que window.opens'ils sont utilisés pendant le traitement d'un événement utilisateur (comme un clic). Dans votre cas, vous appelez window.open plus tard , pas pendant l'événement, car il $.getJSONest asynchrone.

Vous avez deux options:

  1. Faites autre chose, plutôt que window.open.

  2. Rendez l'appel ajax synchrone, ce que vous devriez normalement éviter comme la peste car il verrouille l'interface utilisateur du navigateur. $.getJSONest équivalent à:

    $.ajax({
      url: url,
      dataType: 'json',
      data: data,
      success: callback
    });

    ... et ainsi vous pouvez rendre votre $.getJSONappel synchrone en mappant vos paramètres sur ce qui précède et en ajoutant async: false:

    $.ajax({
        url:      "redirect/" + pageId,
        async:    false,
        dataType: "json",
        data:     {},
        success:  function(status) {
            if (status == null) {
                alert("Error in verifying the status.");
            } else if(!status) {
                $("#agreement").dialog("open");
            } else {
                window.open(redirectionURL);
            }
        }
    });

    Encore une fois, je ne préconise pas les appels ajax synchrones si vous pouvez trouver un autre moyen d'atteindre votre objectif. Mais si vous ne pouvez pas, vous y allez.

    Voici un exemple de code qui échoue au test en raison de l'appel asynchrone:

    Exemple en direct | Source en direct (les liens en direct ne fonctionnent plus en raison des modifications apportées à JSBin)

    jQuery(function($) {
      // This version doesn't work, because the window.open is
      // not during the event processing
      $("#theButton").click(function(e) {
        e.preventDefault();
        $.getJSON("http://jsbin.com/uriyip", function() {
          window.open("http://jsbin.com/ubiqev");
        });
      });
    });

    Et voici un exemple qui fonctionne, en utilisant un appel synchrone:

    Exemple en direct | Source en direct (les liens en direct ne fonctionnent plus en raison des modifications apportées à JSBin)

    jQuery(function($) {
      // This version does work, because the window.open is
      // during the event processing. But it uses a synchronous
      // ajax call, locking up the browser UI while the call is
      // in progress.
      $("#theButton").click(function(e) {
        e.preventDefault();
        $.ajax({
          url:      "http://jsbin.com/uriyip",
          async:    false,
          dataType: "json",
          success:  function() {
            window.open("http://jsbin.com/ubiqev");
          }
        });
      });
    });
TJ Crowder
la source
3
Mille mercis. Votre suggestion de rendre les appels synchrones a fonctionné comme un charme. La même chose m'a aidé dans mon prochain numéro probable de la gestion de la navigation sur l'événement OK du dialogue.
mavaze
Comme TJ Crowder a laissé 2 questions ouvertes, [1. trouver une alternative à window.open] et [2. éviter les appels synchrones ajax] les suggestions sur ceux-ci sont les bienvenues pour le bénéfice de la communauté. Sinon, j'ai trouvé sa réponse une solution parfaite pour ladite question :)
mavaze
4
Pour moi, j'ai ajouté un lien avec target = "_ blank" pour inviter l'utilisateur à cliquer.
hiroshi
1
@Evan: Vous devrez utiliser XHR directement. Il y a un exemple dans le ticket pour désapprouver les requêtes synchrones .
TJ Crowder
2
@mavaze Je pense que vous pouvez éviter les deux problèmes si vous pouvez modifier le flux de contrôle pour ouvrir d'abord la fenêtre, (sync), puis faire la requête AJax (async), puis utiliser la réponse pour afficher le message d'erreur ou faire la redirection.
Stijn de Witt
61

vous pouvez appeler window.open sans blocage du navigateur uniquement si l'utilisateur effectue directement une action. Le navigateur envoie un indicateur et détermine cette fenêtre ouverte par l'action de l'utilisateur.

Donc, vous pouvez essayer ce scénario:

  1. var myWindow = window.open ('')
  2. dessiner n'importe quel message de chargement dans cette fenêtre
  3. une fois la demande terminée, il vous suffit d'appeler myWindow.location = ' http://google.com '
Evgeniy Kubyshin
la source
2
Cela ne fonctionne pas sur Safari Mobile (testé sur IPhone 4). J'ai essayé plusieurs autres idées (par exemple myWindow.postMessage) mais à cause de la restriction de Safaris de ne pas exécuter JavaScript en arrière-plan, la fenêtre parente ne peut jamais envoyer ce changement d'emplacement.
roundrobin
@BausTheBig j'ai testé sur IPhone 6, IOS8. Ça a marché comme sur des roulettes.
lvarayut
recherchait un moyen de suivre les liens externes avec Google Analytics sans bloquer les fenêtres contextuelles. C'était exactement ce dont j'avais besoin. Semble fonctionner correctement sur iPhone 4s sous iOS7 (téléphone réel) et iOS 8 (simulateur iOS).
notacouch
37

J'ai eu ce problème et je n'avais pas mon URL prête jusqu'à ce que le rappel renvoie des données. La solution était d'ouvrir une fenêtre vide avant de démarrer le rappel, puis de définir simplement l'emplacement lorsque le rappel revient.

$scope.testCode = function () {
    var newWin = $window.open('', '_blank');
    service.testCode().then(function (data) {
        $scope.testing = true;
        newWin.location = '/Tests/' + data.url.replace(/["]/g, "");
    });
};
KnuturO
la source
1
J'ai utilisé cette solution pour ouvrir une nouvelle fenêtre et contourner le bloqueur de popup
VeldMuijz
Et si je ne sais pas à l'avance si je dois ouvrir une fenêtre contextuelle? Comme dans: action().then(doWindowOpenThing).catch(doSomethingElseThatDoesntOpenAPopup) Donc je ne peux pas ouvrir une fenêtre avant la main (pour garder sa référence, etc.) si plus tard je ne l'utilise pas, non?
Luiz
Non, cela ouvrirait un nouvel onglet à chaque fois et je suppose que vous ne voudriez pas cela dans le cas où vous ne l'utilisez pas. Le fait est que le navigateur vous permettra uniquement d'ouvrir un nouvel onglet dans la fonction de clic (action initiée par l'utilisateur), sinon le bloqueur de fenêtres contextuelles verra cela comme un script ouvrant un nouvel onglet sans l'autorisation de l'utilisateur et le bloquera.
KnuturO
Merci de tester si newWin est nul!
FrancescoMM
Pourquoi ? Je ne vois aucune raison à cela.
KnuturO
18

essayez ça, ça marche pour moi,

$('#myButton').click(function () {
    var redirectWindow = window.open('http://google.com', '_blank');
    $.ajax({
        type: 'POST',
        url: '/echo/json/',
        success: function (data) {
            redirectWindow.location;
        }
    });
});

Est un violon pour cela http://jsfiddle.net/safeeronline/70kdacL4/1/

Mohammed Safeer
la source
1
Merci, a travaillé pour moi aussi - sans ce violon, je ne l'aurais probablement pas cru, lol!
rmcsharry
3
Cela devrait être la réponse acceptée! Vous pouvez également utiliser redirectWindow.location.assign ('new_url') si vous avez besoin de données asynchrones pour créer l'url.
matheusr
Belle solution,
continuez comme
S'il vous plaît, faites un test si redirectWindow est nul! Selon les différentes options du navigateur, vous pouvez être bloqué ou non.
FrancescoMM
8

Windows doit être créé sur la même pile (aka microtask ) que l'événement initié par l'utilisateur, par exemple un rappel de clic - afin qu'ils ne puissent pas être créés plus tard, de manière asynchrone.

Cependant, vous pouvez créer une fenêtre sans URL et vous pouvez ensuite modifier l'URL de cette fenêtre une fois que vous la connaissez , même de manière asynchrone!

window.onclick = () => {
  // You MUST create the window on the same event
  // tick/stack as the user-initiated event (e.g. click callback)
  const googleWindow = window.open();

  // Do your async work
  fakeAjax(response => {
    // Change the URL of the window you created once you
    // know what the full URL is!
    googleWindow.location.replace(`https://google.com?q=${response}`);
  });
};

function fakeAjax(callback) {
  setTimeout(() => {
    callback('example');
  }, 1000);
}

Les navigateurs modernes ouvriront la fenêtre avec une page vierge (souvent appelée about:blank), et en supposant que votre tâche asynchrone pour obtenir l'URL soit assez rapide, l'UX qui en résulte est généralement bonne. Si vous souhaitez plutôt afficher un message de chargement (ou quoi que ce soit) dans la fenêtre pendant que l'utilisateur attend, vous pouvez utiliser des URI de données .

window.open('data:text/html,<h1>Loading...<%2Fh1>');
Jayphelps
la source
3

Ce code m'aide. J'espère que cela aidera certaines personnes

$('formSelector').submit( function( event ) {

    event.preventDefault();

    var newWindow = window.open('', '_blank', 'width=750,height=500');

    $.ajax({

        url: ajaxurl,
        type: "POST",
        data: { data },

    }).done( function( response ) {

        if ( ! response ) newWindow.close();
        else newWindow.location = '/url';

    });
});
Richard
la source
3

Essayez d'utiliser un élément de lien et cliquez dessus avec javascriipt

<a id="SimulateOpenLink" href="#" target="_blank" rel="noopener noreferrer"></a>

et le script

function openURL(url) {
    document.getElementById("SimulateOpenLink").href = url
    document.getElementById("SimulateOpenLink").click()
}

Utilisez-le comme ça

//do stuff
var id = 123123141;
openURL("/api/user/" + id + "/print") //this open webpage bypassing pop-up blocker
openURL("https://www.google.com") //Another link
Fernando Carvajal
la source
Cela a évité de traiter la fenêtre vide pour moi.
ponder275
Il s'est avéré que cela n'a pas résolu mon problème avec l'iPhone bloquant ma fenêtre en tant que pop-up.
ponder275
2

L'observation que l'événement devait être initié par l'utilisateur m'a aidé à comprendre la première partie de cela, mais même après cela, Chrome et Firefox ont toujours bloqué la nouvelle fenêtre. La deuxième partie ajoutait target = "_ blank" au lien, qui a été mentionné dans un commentaire.

En résumé: vous devez appeler window.open à partir d'un événement initié par l'utilisateur, dans ce cas en cliquant sur un lien, et ce lien doit avoir target = "_ blank".

Dans l'exemple ci-dessous, le lien utilise class = "button-twitter".

$('.button-twitter').click(function(e) {
  e.preventDefault();
  var href = $(this).attr('href');
  var tweet_popup = window.open(href, 'tweet_popup', 'width=500,height=300');
});
Alexis Bellido
la source
2
var url = window.open("", "_blank");
url.location = "url";

cela a fonctionné pour moi.

Diego Santa Cruz Mendezú
la source
1

J'utilise cette méthode pour éviter le bloqueur de popup dans mon code React. cela fonctionnera également dans tous les autres codes javascript.

Lorsque vous effectuez un appel asynchrone lors d'un événement de clic, ouvrez d'abord une fenêtre vide, puis écrivez l'URL dans celle-ci plus tard, lorsqu'un appel asynchrone se terminera.

const popupWindow = window.open("", "_blank");
popupWindow.document.write("<div>Loading, Plesae wait...</div>")

en cas de succès de l'appel asynchrone, écrivez ce qui suit

popupWindow.document.write(resonse.url)
Tabish
la source