Ignore o bloqueador de pop-up em window.open quando JQuery event.preventDefault () estiver definido

98

Quero mostrar uma caixa de diálogo JQuery condicionalmente no evento de clique de um hiperlink.

Eu tenho um requisito como na condição1 abrir um diálogo JQuery e se a condição1 não for satisfeita, navegue até a página conforme referenciado pela tag 'href' cujo evento de clique está em questão.

Consigo chamar uma função no evento de clique do link. Esta função agora verifica a referida condição executando outra URL (que executa meu controlador Spring e retorna uma resposta).

Tudo funciona perfeitamente com apenas window.open sendo bloqueado pelo bloqueador de pop-up.

$('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);
        }
    });
});

Se eu remover e.preventDefault();do código, o bloqueador de popoup não bloqueia a página, no entanto, para a condição1, ele abre a caixa de diálogo, bem como abre a página 'href'.

Se eu resolver um, criará problemas para outro. Não sou capaz de fazer justiça a ambas as condições simultaneamente.

Você poderia me ajudar a resolver este problema, por favor?

Assim que isso for resolvido, tenho outro problema para resolver, ou seja, a navegação no evento OK do diálogo :)

labirinto
fonte
1
tente não usar .live está obsoleto e um ponteiro para .on ()!
mas-designs de
@EvilP: Como outros disseram a você da última vez, apenas no 1.7 e acima. Um monte de projetos ainda estará em 1.5 ou 1.6.
TJ Crowder
10
Downvoter: Só porque você não gosta de pop-ups, isso não significa que esta questão deva ser votada para baixo. Uma pergunta deve ser rejeitada se "... não mostrar nenhum esforço de pesquisa; se não for clara ou se não for útil" . A pergunta é clara, não parece preguiçosa e decorre de uma combinação não trivial de fatores.
TJ Crowder
@TJCrowder: Ele não especificou qual versão está usando. Portanto, devo considerar que ele está usando a versão mais recente. Se ele estiver usando uma versão diferente, tenho certeza de que ele mencionará isso porque ENTÃO ele ESTARIA ciente do fato de que o live está obsoleto e explicaria POR QUE ele está usando .live (). Ninguém nunca me disse a "última" vez, então acho que você deveria dar um tempo e se acalmar. Não há necessidade de ser tão duro ...
mas-designs
Não recebi nenhuma notificação sobre isso. Mas não é normal considerar que se alguém não especifica sua versão considerar que está usando a mais recente? Quer dizer, tenho que usar 1.3 por um motivo e estou ciente do fato de que há uma versão mais nova e melhor.
mas-designs de

Respostas:

144

Os bloqueadores de pop-up normalmente só permitirão window.opense usados durante o processamento de um evento do usuário (como um clique). No seu caso, você está ligando window.open mais tarde , não durante o evento, porque $.getJSONé assíncrono.

Você tem duas opções:

  1. Faça outra coisa, em vez de window.open.

  2. Faça a chamada ajax síncrona, que é algo que você normalmente deve evitar como uma praga, pois bloqueia a IU do navegador. $.getJSONé equivalente a:

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

    ... e assim você pode fazer sua $.getJSONchamada síncrona mapeando seus parâmetros para o acima e adicionando 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);
            }
        }
    });
    

    Novamente, eu não defendo chamadas ajax síncronas se você puder encontrar qualquer outra maneira de atingir seu objetivo. Mas se você não puder, aí está.

    Aqui está um exemplo de código que falha no teste por causa da chamada assíncrona:

    Exemplo ao vivo | Fonte ao vivo (os links ao vivo não funcionam mais devido às mudanças no 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");
        });
      });
    });
    

    E aqui está um exemplo que funciona, usando uma chamada síncrona:

    Exemplo ao vivo | Fonte ao vivo (os links ao vivo não funcionam mais devido às mudanças no 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
fonte
3
Obrigado um milhão. Sua sugestão de tornar a chamada síncrona funcionou perfeitamente. O mesmo me ajudou na minha próxima edição provável de lidar com a navegação no evento OK do diálogo.
mavaze
Como TJ Crowder deixou 2 questões em aberto, [1. encontre alternativa para window.open] e [2. evite a chamada síncrona do Ajax] sugestões sobre esses são bem-vindos para o benefício da comunidade.
Caso contrário,
4
Para mim, adicionei um link com target = "_ blank" para solicitar que o usuário clique.
Hiroshi
1
@Evan: Você terá que usar o XHR diretamente. Há um exemplo no tíquete para descontinuar solicitações síncronas .
TJ Crowder
2
@mavaze Acho que você pode evitar os dois problemas se alterar o fluxo de controle para primeiro abrir a janela (sincronizar), depois fazer a solicitação AJax (assíncrona) e usar a resposta para mostrar a mensagem de erro ou redirecionar.
Stijn de Witt de
61

você pode chamar window.open sem o bloqueio do navegador apenas se o usuário realizar alguma ação diretamente. O navegador envia alguma bandeira e determina a janela aberta pela ação do usuário.

Então, você pode tentar este cenário:

  1. var myWindow = window.open ('')
  2. desenhe qualquer mensagem de carregamento nesta janela
  3. quando a solicitação for concluída, basta chamar myWindow.location = ' http://google.com '
Evgeniy Kubyshin
fonte
2
Isso não funciona no Safari Mobile (testado no IPhone 4). Tentei várias outras idéias (por exemplo myWindow.postMessage), mas devido à restrição do Safaris de não executar JavaScript em segundo plano, a janela pai nunca pode enviar essa mudança de local.
roundrobin de
@BausTheBig eu testei no IPhone 6, IOS8. Funcionou como um encanto.
lvarayut
estava procurando uma maneira de rastrear links externos com o Google Analytics sem bloquear o pop-up. Isso era exatamente o que eu precisava. Parece funcionar bem no iPhone 4s no iOS7 (telefone real) e iOS 8 (simulador iOS).
notacouch
37

Eu tive esse problema e não tinha meu url pronto até que o retorno de chamada retornasse alguns dados. A solução foi abrir a janela em branco antes de iniciar o retorno de chamada e então apenas definir o local quando o retorno de chamada retornar.

$scope.testCode = function () {
    var newWin = $window.open('', '_blank');
    service.testCode().then(function (data) {
        $scope.testing = true;
        newWin.location = '/Tests/' + data.url.replace(/["]/g, "");
    });
};
KnuturO
fonte
1
Usei esta solução para abrir uma nova janela e ignorar o popupblocker
VeldMuijz
E se eu não souber de antemão se vou precisar abrir um pop-up? Tipo: action().then(doWindowOpenThing).catch(doSomethingElseThatDoesntOpenAPopup) então não posso abrir uma janela antes (para guardar a referência, etc) se depois não vou mais usar, certo?
Luiz
Não, ele abriria uma nova guia toda vez e eu acho que você não iria querer isso no caso de não usá-lo. O fato é que o navegador só permitirá que você abra uma nova guia na função de clique (ação iniciada pelo usuário), caso contrário, o bloqueador de pop-up verá isso como um script abrindo uma nova guia sem a permissão do usuário e o bloqueará.
KnuturO de
Por favor, teste se newWin é nulo!
FrancescoMM
Por quê ? Não vejo razão para isso.
KnuturO
18

tente isso, funciona para mim,

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

É violar este http://jsfiddle.net/safeeronline/70kdacL4/1/

Mohammed Safeer
fonte
1
Obrigado, funcionou para mim também - se não fosse por aquele violino eu provavelmente não teria acreditado, lol!
rmcsharry
3
Esta deve ser a resposta aceita! Você também pode usar redirectWindow.location.assign ('new_url') se precisar de dados assíncronos para construir o url.
matheusr
Boa solução
Continue assim
Por favor, teste se redirectWindow é nulo! De acordo com as diferentes opções do navegador, você pode ser bloqueado ou não.
FrancescoMM
8

O Windows deve ser criado na mesma pilha (também conhecido como microtarefa ) que o evento iniciado pelo usuário, por exemplo, um retorno de chamada de clique - para que não possam ser criados posteriormente, de forma assíncrona.

No entanto, você pode criar uma janela sem um URL e, em seguida, alterar o URL dessa janela assim que o souber , mesmo de forma assíncrona!

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);
}

Os navegadores modernos abrirão a janela com uma página em branco (frequentemente chamada about:blank) e, supondo que sua tarefa assíncrona para obter a URL seja bastante rápida, o UX resultante é geralmente bom. Se você quiser renderizar uma mensagem de carregamento (ou qualquer coisa) na janela enquanto o usuário espera, você pode usar URIs de dados .

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

Este código me ajuda. Espero que ajude algumas pessoas

$('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
fonte
3

Tente usar um elemento de link e clique nele com javascriipt

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

e o roteiro

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

Use assim

//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
fonte
Isso evitou lidar com a janela em branco para mim.
ponder275
Acontece que isso não resolveu meu problema com o iPhone bloqueando minha janela como um pop-up.
ponder275
2

A observação de que o evento tinha que ser iniciado pelo usuário me ajudou a descobrir a primeira parte disso, mas mesmo depois disso o Chrome e o Firefox ainda bloquearam a nova janela. A segunda parte foi adicionar target = "_ blank" ao link, que foi mencionado em um comentário.

Em resumo: você precisa chamar window.open de um evento iniciado pelo usuário, neste caso clicando em um link, e esse link precisa ter target = "_ blank".

No exemplo abaixo, o link está usando 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
fonte
2
var url = window.open("", "_blank");
url.location = "url";

isso funcionou para mim.

Diego Santa Cruz Mendezú
fonte
1

Estou usando esse método para evitar o bloqueador de pop-up em meu código React. funcionará em todos os outros códigos javascript também.

Quando você estiver fazendo uma chamada assíncrona no evento de clique, apenas abra uma janela em branco primeiro e depois escreva o URL quando a chamada assíncrona for concluída.

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

sobre o sucesso da chamada assíncrona, escreva o seguinte

popupWindow.document.write(resonse.url)
Tabish
fonte