Evite los bloqueadores de ventanas emergentes del navegador

172

Estoy desarrollando un flujo de autenticación OAuth puramente en JavaScript y quiero mostrarle al usuario la ventana "conceder acceso" en una ventana emergente, pero se bloquea.

¿Cómo puedo evitar que las ventanas emergentes creadas por cualquiera window.openo que window.showModalDialogsean bloqueadas por los bloqueadores de ventanas emergentes de los diferentes navegadores?

Pablo Fernández
fuente
11
Incluso si fuera posible (no lo sé), si las personas usan bloqueadores de ventanas emergentes, debes respetarlo. La mayoría de los navegadores muestran un mensaje cuando un sitio intenta abrir una ventana emergente para que puedan verlo si lo desean. Puede poner un comentario en su sitio de que parte del contenido se abre en una ventana emergente y el usuario debe permitirlo para continuar.
Felix Kling
55
La mejor práctica se vería así: 1) Haz esto con éxito 2) Cierra las ventanas y cierra la puerta y cúbrete con miedo de la multitud reunida de clientes web molestos 3) Arrepiéntete, quita el busta-busta emergente y respeta tu audiencia
Alex Mcp
Alex y Felix, he actualizado la pregunta. No usaré el conocimiento para el mal :). ¡Gracias!
Pablo Fernández
9
Me gustaría agregar que omitir un bloqueador de ventanas emergentes puede estar tratando de mejorar la experiencia del usuario. En un ejemplo en el que estoy trabajando ahora, estamos usando una aplicación Javascript (basada en ExtJS) y estamos tratando de permitir que los usuarios paguen con PayPal. Les estamos dando un botón en el que pueden hacer clic para iniciar PayPal en una nueva ventana, pero ciertas versiones de IE lo bloquean como una ventana emergente (aunque sea un clic de botón). Si ahora habilitan la ventana emergente, la pantalla se vuelve a cargar y, como aplicación de JavaScript, perdemos el estado de la ventana y tienen que comenzar de nuevo. Así que realmente: el problema es que IE es tonto.
NateDSaint
1
@FelixKling Sí, es posible. Los fabricantes de navegadores ya pensaron en esto. Abrir una ventana emergente está bien siempre que haya una intención del usuario (señalado por el usuario haciendo clic en un enlace o botón). Los bloqueadores de ventanas emergentes deben respetar la intención del usuario. Si el bloqueador de ventanas emergentes de IE no lo hace, es el bloqueador de ventanas emergentes el que tiene la culpa. Los usuarios usan bloqueadores de ventanas emergentes para evitar que los scripts abran ventanas emergentes a voluntad, no para bloquear ventanas emergentes que ellos mismos intentaron abrir (haciendo clic en un botón o enlace).
Stijn de Witt

Respuestas:

286

La regla general es que los bloqueadores de ventanas emergentes se activarán si window.opense invoca o similar desde JavaScript que no es invocado por la acción directa del usuario . Es decir, puede llamar window.openen respuesta a un clic en el botón sin ser golpeado por el bloqueador de ventanas emergentes, pero si coloca el mismo código en un evento de temporizador, se bloqueará. La profundidad de la cadena de llamadas también es un factor: algunos navegadores antiguos solo miran a la persona que llama inmediatamente, los navegadores más nuevos pueden retroceder un poco para ver si la persona que llamó fue un clic del mouse, etc. Manténgalo lo más superficial posible para evitar los bloqueadores de ventanas emergentes.

dthorpe
fuente
2
Curiosamente, las ventanas emergentes iniciadas a través de un evento de cambio vinculado a un elemento seleccionado se bloquearán (en Chrome, no en FF), aunque ese evento sea iniciado por una acción directa del usuario, como un clic. Aunque si están vinculados a una entrada, están permitidos. Extraño.
ccnokes
66
Nadie dijo que el navegador fuera consistente. : P
dthorpe
8
A través de los experimentos, tengo que entender que la profundidad de la pila no tiene nada que ver con el bloqueador de ventanas emergentes. En realidad, comprueba si se llama a window.open dentro de 1 segundo después de la acción del usuario o no. Probado en Chrome 46 y Firefox 42.
Mesqalito
El tiempo de espera de 1 segundo se puede eludir en ambos navegadores, lo que permite una cantidad de tiempo indefinida usando la respuesta de @ tj-crowder en esta página ... haga que la URL que está llamando a través de ajax sea un php (o lo que sea) que use sleep ( ) durante un período de tiempo antes de que devuelva una respuesta. El navegador se bloqueará mientras 'carga' la página ... luego activará la ventana emergente cuando reciba una respuesta. Sin embargo, en Chrome, debe agregar una alerta () justo antes del ajax () para que este truco funcione.
fanfarria
184

Sobre la base de Jason Sebring 's consejo muy útil , y en las cosas cubiertos aquí y allí , he encontrado una solución perfecta para mi caso:

Pseudocódigo con fragmentos de Javascript:

  1. crear inmediatamente una ventana emergente en blanco en la acción del usuario

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

    Opcional: agregue algún mensaje de información "en espera". Ejemplos:

    a) Una página HTML externa: reemplace la línea anterior con

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

    b) Texto: agregue la siguiente línea debajo de la anterior:

    importantStuff.document.write('Loading preview...');
  2. llénelo con contenido cuando esté listo (cuando se devuelve la llamada AJAX, por ejemplo)

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

Enriquezca la llamada window.opencon cualquier opción adicional que necesite.

De hecho, uso esta solución para una redirección de mailto, y funciona en todos mis navegadores (Windows 7, Android). El _blankbit ayuda a que la redirección de mailto funcione en dispositivos móviles, por cierto.

¿Tu experiencia? ¿Alguna forma de mejorar esto?

Señor suizo
fuente
1
? Entonces, ¿qué haces si no es por la acción del usuario en el navegador? Para mi ejemplo, después de que el usuario se autentique en el servidor, debe abrir una nueva pestaña
Don Cheadle,
@mmcrae no hagas eso. Sea lo que sea que intente hacer, hay una mejor manera que una ventana emergente. De lo contrario, es probable que nunca quiera ver su sitio. Los navegadores de hoy se aseguran de que no pueda hacerlo, vea la respuesta de @dthorpe .
Señor suizo
55
Whatever it is you intend to do, there is a better way than a popup windowjajaja Extraña generalización. Al cliente le gustaría que la página en la que se autenticaron permanezca abierta, y que el nuevo sitio / página de destino después de la autenticación se abra en una nueva pestaña. Sí, no es mi idea de una excelente experiencia de usuario ... pero parece razonable
Don Cheadle
@mmcrae. Siéntate y piensa, en serio. Te prometo que encontrarás una "acción del usuario" en el escenario que describas. El punto principal de mi respuesta es que crea la ventana emergente cuando tiene la acción del usuario , y luego la llena con contenido más adelante. Sugerencia para su caso: el usuario pulsa "autenticar" -> crea la ventana emergente (vacía); el temporizador está activo o la respuesta del servidor está de regreso o lo que sea -> llene el contenido en la ventana emergente
Señor suizo
3
Consejo útil, si la solicitud ajax falla, llame importantStuff.closepara cerrar la nueva pestaña y proporcionar y alertar en la página original.
Goose
24

Además, la publicación Swiss Mister, en mi caso, el window.open se lanzó dentro de una promesa, que activó el bloqueador de ventanas emergentes, mi solución fue: en angular:

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

así es como puede abrir una nueva pestaña utilizando la respuesta de promesa y no invocando el bloqueador de ventanas emergentes.

David
fuente
1
Esto llevó a mi solución! Donde creé una variable que contenía la pestaña abierta, y luego llené la url después de que se cargaron los datos. Gracias. const tab = window.open(); observable.subscribe(dataUrl => tab.location.href = dataUrl);
jonas
1
Si tiene habilitado el bloqueador de elementos emergentes, esto no solucionará el problema con el bloqueador de elementos emergentes ...
Alejandro Vales
La solución @Alejandro Vales jonas solo funcionará si el código se ejecutará desde un evento onClick o cualquier interacción del usuario. Cuando intentas abrir una nueva pestaña a través de windows. Abrir dentro de una promesa, tiempo de espera, suscribirse ... el navegador piensa que es una manipulación para el usuario, por lo que la solución es crear una instancia antes de ingresar la promesa, dentro de ti solo cambias the href como lo hizo jonas
David
@David Muchas gracias !!! Por casualidad no vi la .thenrespuesta y por eso me confundí tanto: / SRY ...
Alejandro Vales
@David ¡Esto es genial! Funciona de maravilla. Si pudiera molestarte con una pregunta más, ¿sabes cómo hacer que esto funcione también en Edge? Un empujón a un recurso adecuado sería de gran ayuda ...
dzenesiz
21

Como buena práctica, creo que es una buena idea probar si una ventana emergente fue bloqueada y tomar medidas por si acaso. Debe saber que window.open tiene un valor de retorno, y ese valor puede ser nulo si la acción falla. Por ejemplo, en el siguiente código:

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 la ventana emergente está bloqueada, window.open devolverá nulo. Entonces la función devolverá falso.

Como ejemplo, imagine llamar a esta función directamente desde cualquier enlace con target="_blank": si la ventana emergente se abre con éxito, el retorno falsebloqueará la acción del enlace; de ​​lo contrario, si la ventana emergente está bloqueada, el retorno truepermitirá el comportamiento predeterminado (abrir una nueva ventana en blanco) y continuar .

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

De esta manera, tendrá una ventana emergente si funciona, y una ventana _blank si no.

Si la ventana emergente no se abre, puede:

  • abra una ventana en blanco como en el ejemplo y continúe
  • abrir una ventana emergente falsa (un iframe dentro de la página)
  • informar al usuario ("permita ventanas emergentes para este sitio")
  • abra una ventana en blanco y luego informe al usuario, etc.
FrancescoMM
fuente
Gracias. ¡Trabajó!
Bcktr
8

de la API de JavaScript oauth de Google:

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

Vea el área donde se lee:

Configuración de autenticación

La implementación del cliente de OAuth 2.0 utiliza una ventana emergente para solicitar al usuario que inicie sesión y apruebe la aplicación. La primera llamada a gapi.auth.authorize puede activar bloqueadores de ventanas emergentes, ya que abre la ventana emergente indirectamente. Para evitar que el bloqueador de ventanas emergentes se active en llamadas de autenticación, llame a gapi.auth.init (devolución de llamada) cuando se carga el cliente. La devolución de llamada suministrada se ejecutará cuando la biblioteca esté lista para realizar llamadas de autenticación.

Supongo que está relacionado con la respuesta real anterior en cómo explica que si hay una respuesta inmediata, no activará la alarma emergente. El "gapi.auth.init" está haciendo que la API ocurra de inmediato.

Aplicación práctica

Hice un microservicio de autenticación de código abierto usando el pasaporte de nodo en npm y los diversos paquetes de pasaportes para cada proveedor. Utilicé un enfoque de redireccionamiento estándar para la tercera parte, dándole una URL de redireccionamiento para volver. Esto fue programático, por lo que podría tener diferentes lugares para redirigir de nuevo si inicio de sesión / registro y en páginas particulares.

github.com/sebringj/athu

passportjs.org

Jason Sebring
fuente
3

Probé varias soluciones, pero esta es la única que realmente funcionó para mí en todos los navegadores.

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

pomobc
fuente
2
Esto simplemente no funciona para las personas que tienen habilitado el bloqueador de ventanas emergentes
Alejandro Vales
lo que es url? No está asignado.
shinriyo
@shinriyo url es el enlace a la página a la que desea acceder, por ejemplohttp://example.com
pomobc
@AlejandroVales tengo habilitado el bloqueador de ventanas emergentes y funciona. El problema es que window.open()no se puede abrir una nueva ventana mediante programación y, si es necesario, esa respuesta es una de las opciones. Con la newTabvariable a la que hacemos referencia window.open()y luego podemos llamarla, insertar la urlque necesitamos, en mi caso url de algún blob, y la nueva pestaña se abrirá con la url ingresada.
stanimirsp
0

No quería hacer la nueva página a menos que la devolución de llamada devuelve correctamente, así que hice esto para simular el clic del usuario:

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);
  });
}
usuario3479425
fuente
3
no funciona para mí en Chrome 62. La ventana emergente (página) se bloquea.
s.ermakovich
1
Sí, tienes razón, también he encontrado que esto es inconsistente. Terminé haciendo mi llamada API sincrónica
user3479425
-8

La forma más fácil de deshacerse de esto es:

  1. No use document.open ().
  2. En su lugar, use this.document.location.href = location; donde ubicación es la url que se cargará

Ej:

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

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

Esto funcionó muy bien para mí. Salud

Pramod Shetty
fuente
2
¿Qué hay de abrir la URL en una nueva pestaña?
3 reglas