100 votes

Contournement du bloqueur de fenêtres pop-up sur window.open lorsque JQuery event.preventDefault() est défini

Je veux afficher un dialogue JQuery conditionnellement à l'événement de clic d'un hyperlien.

J'ai une exigence telle que, à la condition 1, ouvrir un dialogue JQuery et, si la condition 1 n'est pas satisfaite, naviguer vers la page référencée par la balise 'href' dont l'événement de clic est en question.

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

Tout fonctionne parfaitement, seule la fonction window.open étant bloquée par le bloqueur de pop-ups.

$('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 retire e.preventDefault(); à partir du code, le bloqueur de popup ne bloque pas la page, mais pour la condition 1, il ouvre le dialogue ainsi que la page 'href'.

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

Pouvez-vous m'aider à résoudre ce problème ?

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

1 votes

Essayez de ne pas utiliser .live qui est déprécié et un pointeur vers .on() !

0 votes

@EvilP : Comme d'autres vous l'ont dit la dernière fois, seulement en 1.7 et plus. A lot des projets seront encore sur 1.5 ou 1.6.

10 votes

Downvoter : Ce n'est pas parce que vous n'aimez pas les fenêtres pop-up que cette question doit être déclassée. Une question doit être déclassée si elle "...ne montre aucun effort de recherche ; si n'est pas clair ou pas utile" . La question est claire, ne semble pas paresseuse et découle d'une combinaison non triviale de facteurs.

145voto

T.J. Crowder Points 285826

Les bloqueurs de fenêtres publicitaires ne permettent généralement window.open si utilisé 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 $.getJSON est 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 cela bloque l'interface utilisateur du navigateur. $.getJSON est équivalent à :

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

    ...et ainsi vous pouvez faire votre $.getJSON l'appel synchrone en faisant correspondre vos paramètres à 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 le pouvez pas, voilà.

    Voici un exemple de code qui échoue au test à cause de l'appel asynchrone :

    Exemple concret | Source en direct (Les liens en direct ne fonctionnent plus à cause des changements apportés à 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 concret | Source en direct (Les liens en direct ne fonctionnent plus à cause des changements apportés à 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");
          }
        });
      });
    });

3 votes

Merci beaucoup. Votre suggestion de rendre l'appel synchrone a fonctionné à merveille. La même chose m'a aidé dans mon prochain problème probable de gestion de la navigation sur l'événement OK du dialogue.

0 votes

Comme T.J. Crowder a laissé 2 questions ouvertes, [1. trouver une alternative à window.open] et [2. éviter l'appel synchrone ajax] les suggestions sur ces questions sont les bienvenues pour le bénéfice de la communauté. En outre, j'ai trouvé que sa réponse était une solution parfaite à la question posée :)

4 votes

Pour moi, j'ai ajouté un lien avec target="_blank" pour inciter l'utilisateur à cliquer.

61voto

Vous pouvez appeler window.open sans que le navigateur ne bloque uniquement si l'utilisateur effectue directement une action. Le navigateur envoie un drapeau et détermine que la fenêtre a été ouverte par l'action de l'utilisateur.

Vous pouvez donc essayer ce scénario :

  1. var myWindow = window.open('')
  2. afficher tout message de chargement dans cette fenêtre
  3. lorsque la demande est effectuée, il suffit d'appeler myWindow.location = '. http://google.com '

2 votes

Cela ne fonctionne pas sur Safari Mobile (testé sur IPhone 4). J'ai essayé plusieurs autres idées (par ex. myWindow.postMessage ) mais, en raison de la restriction de Safaris qui interdit l'exécution de JavaScript en arrière-plan, la fenêtre mère ne peut jamais envoyer ce changement d'emplacement.

0 votes

@BausTheBig J'ai testé sur IPhone 6, IOS8. Ça a marché comme sur des roulettes.

0 votes

Je cherchais un moyen de suivre les liens externes avec Google Analytics sans que les popups soient bloquées. C'est exactement ce dont j'avais besoin. Il semble fonctionner correctement sur l'iPhone 4s sous iOS7 (téléphone réel) et iOS 8 (simulateur iOS).

39voto

KnuturO Points 201

J'ai eu ce problème et je n'avais pas mon url prête jusqu'à ce que le callback retourne des données. La solution était d'ouvrir une fenêtre vide avant de lancer le callback et ensuite de définir l'emplacement lorsque le callback revient.

$scope.testCode = function () {
    var newWin = $window.open('', '_blank');
    service.testCode().then(function (data) {
        $scope.testing = true;
        newWin.location = '/Tests/' + data.url.replace(/["]/g, "");
    });
};

1 votes

J'ai utilisé cette solution pour ouvrir une nouvelle fenêtre et contourner le popupblocker.

0 votes

Que faire si je ne sais pas à l'avance si j'aurai besoin d'ouvrir une fenêtre contextuelle ? Comme dans : action().then(doWindowOpenThing).catch(doSomethingElseThatDo‌​esntOpenAPopup) Je ne peux donc pas ouvrir une fenêtre à l'avance (pour conserver sa référence, etc.) si je ne l'utiliserai pas par la suite, n'est-ce pas ?

0 votes

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 problème est que le navigateur ne vous permet d'ouvrir un nouvel onglet que dans la fonction de clic (action initiée par l'utilisateur), sinon le bloqueur de popup verra cela comme un script ouvrant un nouvel onglet sans la permission de l'utilisateur et le bloquera.

17voto

Mohammed Safeer Points 1533

Essayez ceci, ç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-ce que fiddle sert à cela http://jsfiddle.net/safeeronline/70kdacL4/1/

1 votes

Merci, ça a marché pour moi aussi - si ce n'était pas pour ce violon, je ne l'aurais probablement pas cru, lol !

3 votes

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 construire l'url.

0 votes

Belle solution. Continuez comme ça.

8voto

jayphelps Points 10591

Les fenêtres doivent être créées sur la même pile (alias micro-tâche ) comme l'événement initié par l'utilisateur, par exemple un callback 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 alors changer l'URL de cette fenêtre une fois que vous le savez 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 ouvrent la fenêtre avec une page blanche (souvent appelée about:blank ), et en supposant que votre tâche asynchrone pour obtenir l'URL est assez rapide, l'UX qui en résulte est en général bien. Si vous souhaitez plutôt afficher un message de chargement (ou quoi que ce soit d'autre) dans la fenêtre pendant que l'utilisateur attend, vous pouvez utiliser l'option URI de données .

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X