Omitir el bloqueador de ventanas emergentes en window.open cuando se establece JQuery event.preventDefault ()

98

Quiero mostrar un diálogo de JQuery condicionalmente en el evento de clic de un hipervínculo.

Tengo un requisito como en condition1 abrir un diálogo de JQuery y si condition1 no se satisface, navegue a la página como se hace referencia en la etiqueta 'href' de cuyo evento de clic está en cuestión.

Puedo llamar a una función en el evento de clic del enlace. Esta función ahora verifica dicha condición ejecutando otra URL (que ejecuta mi controlador Spring y devuelve la respuesta).

Todo funciona perfectamente con solo window.open bloqueado por el bloqueador de ventanas emergentes.

$('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 e.preventDefault();elimino del código, el bloqueador de popup no bloquea la página, sin embargo, para la condición 1, abre el diálogo y abre la página 'href'.

Si resuelvo uno, crea problemas para otro. No puedo hacer justicia a ambas condiciones simultáneamente.

¿Podría ayudarme a resolver este problema, por favor?

Una vez que esto se resuelve, tengo otro problema que resolver, es decir, la navegación en el evento OK del diálogo :)

laberinto
fuente
1
¡intente no usar .live está obsoleto y un puntero a .on ()!
mas-designs
@EvilP: Como otros te dijeron la última vez, solo en 1.7 y versiones posteriores. Una gran cantidad de proyectos todavía estará en 1.5 o 1.6.
TJ Crowder
10
Votante negativo: El hecho de que no le gusten las ventanas emergentes no significa que esta pregunta deba ser rechazada. Una pregunta debe ser rechazada si "... no muestra ningún esfuerzo de investigación; si no es clara o no es útil" . La pregunta es clara, no parece floja y se deriva de una combinación de factores no trivial.
TJ Crowder
@TJCrowder: No especificó qué versión está usando. Entonces debo considerar que está usando la última versión. Si está usando una versión diferente, estoy seguro de que lo mencionaría porque ENTONCES SERÍA consciente del hecho de que live está desaprobado y explicaría POR QUÉ está usando .live (). Nadie me dijo la "última" vez, así que creo que deberías tomarte un descanso y calmarte. No hay necesidad de ser tan duro ...
mas-designs
No recibí ninguna notificación sobre eso. ¿Pero no es normal considerar que si alguien no especifica su versión se considere que está usando la última? Quiero decir, tengo que usar 1.3 por una razón y soy consciente del hecho de que hay una versión más nueva y mejor.
mas-designs

Respuestas:

144

Los bloqueadores de ventanas emergentes normalmente solo permitirán window.opensi se usan durante el procesamiento de un evento de usuario (como un clic). En su caso, está llamando window.open más tarde , no durante el evento, porque $.getJSONes asincrónico.

Tienes dos opciones:

  1. Haz algo más, en lugar de window.open.

  2. Haga que la llamada ajax sea sincrónica, que es algo que normalmente debe evitar como la plaga, ya que bloquea la interfaz de usuario del navegador. $.getJSONes equivalente a:

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

    ... y para que pueda $.getJSONsincronizar su llamada asignando sus parámetros a lo anterior y agregando 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);
            }
        }
    });
    

    Una vez más, no defiendo las llamadas ajax sincrónicas si puede encontrar otra forma de lograr su objetivo. Pero si no puede, ahí lo tiene.

    A continuación, se muestra un ejemplo de código que no supera la prueba debido a la llamada asincrónica:

    Ejemplo en vivo | Fuente en vivo (los enlaces en vivo ya no funcionan debido a cambios en 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");
        });
      });
    });
    

    Y aquí hay un ejemplo que funciona, usando una llamada sincrónica:

    Ejemplo en vivo | Fuente en vivo (los enlaces en vivo ya no funcionan debido a cambios en 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
fuente
3
Un millón de gracias. Tu sugerencia de sincronizar las llamadas funcionó a las mil maravillas. Lo mismo me ayudó en mi próximo número probable de manejo de la navegación en el evento OK del diálogo.
mavaze
Como TJ Crowder ha dejado dos preguntas abiertas, [1. buscar alternativa a window.open] y [2. evitar llamadas sincrónicas ajax] las sugerencias sobre estas son bienvenidas para beneficio de la comunidad. De lo contrario, encontré su respuesta como una solución perfecta para dicha pregunta :)
mavaze
4
Para mí, agregué un enlace con target = "_ blank" para pedirle al usuario que haga clic.
hiroshi
1
@Evan: Tendrás que usar XHR directamente. Hay un ejemplo en el ticket para desaprobar las solicitudes síncronas .
TJ Crowder
2
@mavaze Creo que puede evitar ambos problemas si puede cambiar el flujo de control para abrir primero la ventana, (sincronizar), luego hacer la solicitud AJax (async) y luego usar la respuesta para mostrar el mensaje de error o hacer la redirección.
Stijn de Witt
61

puede llamar a window.open sin bloquear el navegador solo si el usuario realiza alguna acción directamente. El navegador envía una bandera y determina la ventana abierta por la acción del usuario.

Entonces, puedes probar este escenario:

  1. var myWindow = ventana.open ('')
  2. dibuja cualquier mensaje de carga en esta ventana
  3. cuando se complete la solicitud, simplemente llame a myWindow.location = ' http://google.com '
Evgeniy Kubyshin
fuente
2
Esto no funciona en Safari Mobile (probado en iPhone 4). Probé varias otras ideas (por ejemplo myWindow.postMessage), pero debido a la restricción de Safaris de no ejecutar JavaScript en segundo plano, la ventana principal nunca puede enviar ese cambio de ubicación.
ida
@BausTheBig Probé en IPhone 6, IOS8. Funcionó a las mil maravillas.
lvarayut
estaba buscando una forma de rastrear enlaces externos con Google Analytics sin que se bloqueara la ventana emergente. Eso era exactamente lo que necesitaba. Parece funcionar bien en iPhone 4s en iOS7 (teléfono real) e iOS 8 (iOS Simulator).
notacouch
37

Tuve este problema y no tenía mi URL lista hasta que la devolución de llamada devolviera algunos datos. La solución fue abrir una ventana en blanco antes de iniciar la devolución de llamada y luego establecer la ubicación cuando regrese la devolución de llamada.

$scope.testCode = function () {
    var newWin = $window.open('', '_blank');
    service.testCode().then(function (data) {
        $scope.testing = true;
        newWin.location = '/Tests/' + data.url.replace(/["]/g, "");
    });
};
KnuturO
fuente
1
Usé esta solución para abrir una nueva ventana y omitir el bloqueador de
ventanas
¿Qué pasa si no sé de antemano si tendré que abrir una ventana emergente? Como en: action().then(doWindowOpenThing).catch(doSomethingElseThatDoesntOpenAPopup) Entonces no puedo abrir una ventana de antemano (para mantener su referencia, etc.) si más adelante no la usaré, ¿verdad?
Luiz
No, abriría una nueva pestaña cada vez y supongo que no querrás eso en el caso de que no la uses. El problema es que el navegador solo le permitirá abrir una nueva pestaña en la función de clic (acción iniciada por el usuario); de lo contrario, el bloqueador de ventanas emergentes verá esto como un script que abre una nueva pestaña sin permiso del usuario y la bloqueará.
KnuturO
¡Por favor, pruebe si newWin es nulo!
FrancescoMM
Por qué ? No veo ninguna razón para esto.
KnuturO
18

prueba esto, funciona para mí,

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

Es un violín para esto http://jsfiddle.net/safeeronline/70kdacL4/1/

Mohammed Safeer
fuente
1
Gracias, también funcionó para mí - si no fuera por ese violín probablemente no lo hubiera creído, jajaja!
rmcsharry
3
¡Esta debería ser la respuesta aceptada! También puede usar redirectWindow.location.assign ('new_url') si necesita datos asíncronos para construir la URL.
matheusr
Buena solución
Mantenlo
¡Por favor, pruebe si redirectWindow es nulo! Según las diferentes opciones del navegador, puede bloquearse o no.
FrancescoMM
8

Windows debe crearse en la misma pila (también conocida como microtask ) que el evento iniciado por el usuario, por ejemplo, una devolución de llamada de clic, para que no se puedan crear más tarde, de forma asincrónica.

Sin embargo, puede crear una ventana sin una URL y luego puede cambiar la URL de esa ventana una vez que la sepa , ¡incluso de forma asincrónica!

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

Los navegadores modernos abrirán la ventana con una página en blanco (a menudo llamada about:blank), y suponiendo que su tarea asíncrona para obtener la URL sea bastante rápida, la UX resultante es en su mayoría buena. Si, en cambio, desea representar un mensaje de carga (o cualquier cosa) en la ventana mientras el usuario espera, puede usar URI de datos .

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

Este código me ayuda. Espero que esto ayude a algunas personas

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

    });
});
Ricardo
fuente
3

Intente usar un elemento de enlace y haga clic en él con javascriipt

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

y el guion

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

Úselo así

//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
fuente
Esto me evitó lidiar con la ventana en blanco.
ponder275
Resultó que esto no resolvió mi problema con el iPhone bloqueando mi ventana como una ventana emergente.
ponder275
2

La observación de que el usuario tenía que iniciar el evento me ayudó a descubrir la primera parte de esto, pero incluso después de eso, Chrome y Firefox seguían bloqueando la nueva ventana. La segunda parte fue agregar target = "_ blank" al enlace, que se mencionó en un comentario.

En resumen: debe llamar a window.open desde un evento iniciado por el usuario, en este caso haciendo clic en un enlace, y ese enlace debe tener target = "_ blank".

En el siguiente ejemplo, el enlace usa 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
fuente
2
var url = window.open("", "_blank");
url.location = "url";

esto funcionó para mí.

Diego Santa Cruz Mendezú
fuente
1

Estoy usando este método para evitar el bloqueador de ventanas emergentes en mi código React. también funcionará en todos los demás códigos javascript.

Cuando esté realizando una llamada asíncrona al hacer clic en un evento, simplemente abra una ventana en blanco primero y luego escriba la URL en ella cuando se complete una llamada asíncrona.

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

sobre el éxito de la llamada asíncrona, escriba lo siguiente

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