Detectar ventanas emergentes bloqueadas en Chrome

103

Soy consciente de las técnicas de JavaScript para detectar si una ventana emergente está bloqueada en otros navegadores (como se describe en la respuesta a esta pregunta ). Aquí está la prueba básica:

var newWin = window.open(url);

if(!newWin || newWin.closed || typeof newWin.closed=='undefined')
{
    //POPUP BLOCKED
}

Pero esto no funciona en Chrome. La sección "POPUP BLOCKED" nunca se alcanza cuando la ventana emergente está bloqueada.

Por supuesto, la prueba está funcionando hasta cierto punto, ya que Chrome en realidad no bloquea la ventana emergente, sino que la abre en una pequeña ventana minimizada en la esquina inferior derecha que enumera las ventanas emergentes "bloqueadas".

Lo que me gustaría hacer es saber si la ventana emergente fue bloqueada por el bloqueador de ventanas emergentes de Chrome. Intento evitar el rastreo del navegador a favor de la detección de funciones. ¿Hay alguna forma de hacer esto sin que el navegador rastree?

Editar : ahora he intentado hacer uso de newWin.outerHeight, newWin.lefty otras propiedades similares para lograr esto. Google Chrome devuelve todos los valores de posición y altura como 0 cuando la ventana emergente está bloqueada.

Desafortunadamente, también devuelve los mismos valores incluso si la ventana emergente se abre durante un período de tiempo desconocido. Después de un período mágico (un par de segundos en mis pruebas), la información de ubicación y tamaño se devuelve como los valores correctos. En otras palabras, todavía no estoy más cerca de resolver esto. Cualquier ayuda sería apreciada.

Andrew Ensley
fuente
Yoav, la ubicación muestra lo mismo independientemente de si la ventana emergente está bloqueada o no. ¿Alguien más tiene una respuesta que no implique hacer que el usuario espere 3,5 segundos?
Las últimas soluciones de InvisibleBacon y Andy no funcionan en Chrome 10: aparece el mensaje "falló para Chrome" incluso si la ventana emergente de prueba se mostró correctamente. ¿Alguna idea?
Creo que una nueva pregunta estaría en orden, ya que algunas de estas soluciones parecen haber funcionado solo con las primeras versiones de Chrome.
Bryan Field
1
@George Bailey Estoy de acuerdo, pero para ser claros, algunos de ellos funcionan en la versión actual de Chrome (19). La idea original de Andrew de usar outerHeight (o screenX, como han sugerido otros) me está funcionando bien, combinada con el enfoque setTimeout. Pero, sí, tratar de dar sentido a todas estas respuestas fue realmente confuso hasta que hice mis propias pruebas.
regularmike

Respuestas:

66

Bueno, el "momento mágico" del que hablas es probablemente cuando se ha cargado el DOM de la ventana emergente. O podría ser cuando todo (imágenes, CSS externo, etc.) se haya cargado. Puede probar esto fácilmente agregando un gráfico muy grande a la ventana emergente (¡limpie su caché primero!). Si estaba usando un marco de Javascript como jQuery (o algo similar), podría usar el evento ready () (o algo similar) para esperar a que se cargue el DOM antes de verificar el desplazamiento de la ventana. El peligro en esto es que la detección de Safari funciona de manera conflictiva: el DOM de la ventana emergente nunca estará listo () en Safari porque le dará un identificador válido para la ventana que está tratando de abrir, ya sea que realmente se abra o no. (de hecho, creo que el código de prueba emergente anterior no funcionará para safari).

Creo que lo mejor que puede hacer es envolver su prueba en un setTimeout () y darle a la ventana emergente 3-5 segundos para completar la carga antes de ejecutar la prueba. No es perfecto, pero debería funcionar al menos el 95% del tiempo.

Este es el código que utilizo para la detección entre navegadores, sin la parte de Chrome.

function _hasPopupBlocker(poppedWindow) {
    var result = false;

    try {
        if (typeof poppedWindow == 'undefined') {
            // Safari with popup blocker... leaves the popup window handle undefined
            result = true;
        }
        else if (poppedWindow && poppedWindow.closed) {
            // This happens if the user opens and closes the client window...
            // Confusing because the handle is still available, but it's in a "closed" state.
            // We're not saying that the window is not being blocked, we're just saying
            // that the window has been closed before the test could be run.
            result = false;
        }
        else if (poppedWindow && poppedWindow.test) {
            // This is the actual test. The client window should be fine.
            result = false;
        }
        else {
            // Else we'll assume the window is not OK
            result = true;
        }

    } catch (err) {
        //if (console) {
        //    console.warn("Could not access popup window", err);
        //}
    }

    return result;
}

Lo que hago es ejecutar esta prueba desde el padre y envolverla en un setTimeout (), dando a la ventana secundaria 3-5 segundos para cargar. En la ventana secundaria, debe agregar una función de prueba:

prueba de funcionamiento() {}

El detector de bloqueador de ventanas emergentes prueba para ver si la función "prueba" existe como miembro de la ventana secundaria.

AÑADIDO EL 15 DE JUNIO DE 2015:

Creo que la forma moderna de manejar esto sería usar window.postMessage () para que el niño notifique al padre que la ventana se ha cargado. El enfoque es similar (el niño le dice a los padres que está cargado), pero los medios de comunicación han mejorado. Pude hacer este dominio cruzado del niño:

$(window).load(function() {
  this.opener.postMessage({'loaded': true}, "*");
  this.close();
});

El padre escucha este mensaje usando:

$(window).on('message', function(event) {     
  alert(event.originalEvent.data.loaded)
}); 

Espero que esto ayude.

Ringo
fuente
Rich, eres un gurú de las ventanas emergentes de JavaScript. Gracias. Eso es exactamente lo que necesitaba.
Andrew Ensley
4
¿Alguna actualización sobre esto? Parece que ya no funciona ... Específicamente en Chrome
Chris Wagner
Creo que encontré una manera de hacer que esto funcione para las nuevas versiones de Chrome. Vea mi respuesta para más detalles.
InvisibleBacon
2
Básicamente, hay un error en Chrome. Aunque oculta la ventana emergente, aún se ejecuta y aún recupera el objeto de la ventana, por lo que las comprobaciones regulares no funcionan. Aquí está la solución que funcionó para mí: var popup = window.open (url); if (popup) {popup.onload = function () {console.log (popup.innerHeight> 0? 'open': 'bloqueado'); }} else {console.log ('bloqueado'); } Ejemplo de trabajo aquí: jsbin.com/uticev/3
Remy Sharp
1
Esta respuesta ya no es correcta, cámbiela a la respuesta de @Predrag Stojadinović
Lucas B
16

Solo una mejora del snipet de InvisibleBacon (probado en IE9, Safari 5, Chrome 9 y FF 3.6):

var myPopup = window.open("popupcheck.htm", "", "directories=no,height=150,width=150,menubar=no,resizable=no,scrollbars=no,status=no,titlebar=no,top=0,location=no");
if (!myPopup)
    alert("failed for most browsers");
else {
    myPopup.onload = function() {
        setTimeout(function() {
            if (myPopup.screenX === 0) {
                alert("failed for chrome");
            } else {
                // close the test window if popups are allowed.
                myPopup.close();  
            }
        }, 0);
    };
}
Andy
fuente
¿Por qué cerrar la ventana si se permiten las ventanas emergentes? ¿No sería eso cerrar la ventana emergente que quería abrir en primer lugar?
elemjay19
3
Usando jQuery, en lugar de onload, haría $ (myPopup) .ready (). Ejecutar localmente mi IE fue demasiado rápido y ya se había producido "onload".
Matt Connolly
12

La siguiente es una solución de jQuery para verificar el bloqueador de ventanas emergentes. Ha sido probado en FF (v11), Safari (v6), Chrome (v23.0.127.95) e IE (v7 y v9). Actualice la función _displayError para manejar el mensaje de error como mejor le parezca.

var popupBlockerChecker = {
        check: function(popup_window){
            var _scope = this;
            if (popup_window) {
                if(/chrome/.test(navigator.userAgent.toLowerCase())){
                    setTimeout(function () {
                        _scope._is_popup_blocked(_scope, popup_window);
                     },200);
                }else{
                    popup_window.onload = function () {
                        _scope._is_popup_blocked(_scope, popup_window);
                    };
                }
            }else{
                _scope._displayError();
            }
        },
        _is_popup_blocked: function(scope, popup_window){
            if ((popup_window.innerHeight > 0)==false){ scope._displayError(); }
        },
        _displayError: function(){
            alert("Popup Blocker is enabled! Please add this site to your exception list.");
        }
    };

Uso:

var popup = window.open("http://www.google.ca", '_blank');
popupBlockerChecker.check(popup);

¡Espero que esto ayude! :)

Kevin B
fuente
Esto es realmente útil. Gracias por compartir.
Suvendu Shekhar Giri
De nada, Suvendu, ¡me alegra que lo hayas encontrado útil! ¡Feliz codificación! :)
Kevin B
1
Modifiqué este código para pasar dentro / alrededor de la URL que está intentando abrirse. Esto permite que el método _displayError () muestre una alerta (estoy usando toastr) notificando al usuario que hay un problema y proporcionar un enlace en el que se puede hacer clic que evitará la mayoría de los bloqueadores, ya que es un enlace directo. ¡¡Gracias por compartir!!
Tyler Forsythe
@TylerForsythe ¿tiene más información sobre su solución? Me encantaría poder proporcionar un enlace directamente al contenido en el que se pueda hacer clic.
Joshua Dance
1
@JoshuaDance Aquí hay una esencia que acabo de crear para demostrar mi código modificado y cómo lo invoco. ¡Espero eso ayude! gist.github.com/tylerforsythe/452ceaad62f507d7cb7bd7ddbffe650c
Tyler Forsythe
10

La respuesta de Rich ya no funcionará para Chrome. Parece que Chrome en realidad ejecuta cualquier Javascript en la ventana emergente ahora. Terminé verificando un valor de screenX de 0 para verificar si hay ventanas emergentes bloqueadas. También creo que encontré una manera de garantizar que esta propiedad sea definitiva antes de verificar. Esto solo funciona para ventanas emergentes en su dominio, pero puede agregar un controlador de carga como este:

var myPopup = window.open("site-on-my-domain", "screenX=100");
if (!myPopup)
    alert("failed for most browsers");
else {
    myPopup.onload = function() {
        setTimeout(function() {
            if (myPopup.screenX === 0)
                alert("failed for chrome");
        }, 0);
    };
}

Como muchos han informado, la propiedad "screenX" a veces informa un valor distinto de cero para las ventanas emergentes fallidas, incluso después de la carga. También experimenté este comportamiento, pero si agrega el cheque después de un tiempo de espera de cero ms, la propiedad screenX siempre parece generar un valor consistente.

Avíseme si hay formas de hacer que este script sea más sólido. Sin embargo, parece funcionar para mis propósitos.

Tocino Invisible
fuente
No es para mí, onloadnunca dispara.
hoja
9

Esto funcionó para mí:

    cope.PopupTest.params = 'height=1,width=1,left=-100,top=-100,location=no,toolbar=no,menubar=no,scrollbars=no,resizable=no,directories=no,status=no';
    cope.PopupTest.testWindow = window.open("popupTest.htm", "popupTest", cope.PopupTest.params);

    if( !cope.PopupTest.testWindow
        || cope.PopupTest.testWindow.closed
        || (typeof cope.PopupTest.testWindow.closed=='undefined')
        || cope.PopupTest.testWindow.outerHeight == 0
        || cope.PopupTest.testWindow.outerWidth == 0
        ) {
        // pop-ups ARE blocked
        document.location.href = 'popupsBlocked.htm';
    }
    else {
        // pop-ups are NOT blocked
        cope.PopupTest.testWindow.close();
    }

ExternalHeight y outerWidth son para Chrome porque el truco 'about: blank' de arriba ya no funciona en Chrome.

Predrag Stojadinović
fuente
1
Buena captura de los cambios de Chrome y gracias por actualizarlo aquí. Su respuesta debe estar marcada como correcta.
Lucas B
ExternalWidth y outerHeight ya no funcionan en Chrome
Roman
5

Voy a copiar / pegar la respuesta proporcionada aquí: https://stackoverflow.com/a/27725432/892099 por DanielB. funciona en cromo 40 y está muy limpio. sin trucos sucios o esperas.

function popup(urlToOpen) {
  var popup_window=window.open(urlToOpen,"myWindow","toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=yes, resizable=yes, copyhistory=yes, width=400, height=400");            
  try {
    popup_window.focus();   
  }
  catch (e) {
    alert("Pop-up Blocker is enabled! Please add this site to your exception list.");
  }
}
Hamy
fuente
3

¿Qué tal un Promiseenfoque?

const openPopUp = (...args) => new Promise(s => {
  const win = window.open(...args)
  if (!win || win.closed) return s()
  setTimeout(() => (win.innerHeight > 0 && !win.closed) ? s(win) : s(), 200)
})

Y puedes usarlo como el clásico window.open

const win = await openPopUp('popuptest.htm', 'popuptest')
if (!win) {
  // popup closed or blocked, handle alternative case
}

Podría cambiar el código para que falle la promesa en lugar de regresar undefined, solo pensé que ifera un flujo de control más fácil que try / catchen este caso.

kigiri
fuente
Esto funciona para detectar bloqueadores de anuncios de extensiones de Chrome. +1
Micheal C Wallas
2

Compruebe la posición de la ventana en relación con el padre. Chrome hace que la ventana parezca casi fuera de la pantalla.

Jason Cohen
fuente
Lo intentaré y te haré saber mis resultados. Gracias.
Andrew Ensley
Google Chrome informa las compensaciones izquierda y superior como 0 cuando la ventana emergente está "bloqueada". Pensé que este era mi boleto dorado, pero no. También informa las compensaciones como 0 inmediatamente después de la apertura real. En algún momento mágico en el futuro después de la apertura, las compensaciones superior e izquierda se informan correctamente.
Andrew Ensley
Verifique mi publicación de una manera que parezca garantizar que las compensaciones se establezcan antes de verificar.
InvisibleBacon
2

Tuve un problema similar con las ventanas emergentes que no se abren en Chrome. Estaba frustrado porque no estaba tratando de hacer algo furtivo, como una ventana emergente de carga, simplemente abriendo una ventana cuando el usuario hacía clic. Estaba DOBLEMENTE frustrado porque la ejecución de mi función, que incluía window.open () desde la línea de comando de firebug, funcionó, ¡mientras que hacer clic en mi enlace no funcionó! Aquí estaba mi solución:

Manera incorrecta: ejecutar window.open () desde un detector de eventos (en mi caso, dojo.connect al método de evento onclick de un nodo DOM).

dojo.connect(myNode, "onclick", function() {
    window.open();
}

Forma correcta: asignando una función a la propiedad onclick del nodo que llamó a window.open ().

myNode.onclick = function() {
    window.open();
}

Y, por supuesto, todavía puedo hacer detectores de eventos para ese mismo evento onclick si es necesario. Con este cambio, pude abrir mis ventanas a pesar de que Chrome estaba configurado en "No permitir que ningún sitio muestre ventanas emergentes". Alegría.

Si alguien sabio en las formas de Chrome puede decirnos al resto de nosotros por qué marca la diferencia, me encantaría escucharlo, aunque sospecho que es solo un intento de cerrar la puerta a las ventanas emergentes programáticas maliciosas.

Chris
fuente
Gracias por compartir su solución. Funciona. Esta es la mejor y más limpia forma de abrir ventanas emergentes en Chrome. Tu respuesta debería estar en la parte superior. El resto de soluciones son trucos "sucios".
Mandeep Janjua
2

Aquí hay una versión que funciona actualmente en Chrome. Solo una pequeña alteración de la solución de Rich, aunque agregué un contenedor que también maneja el tiempo.

function checkPopupBlocked(poppedWindow) {
 setTimeout(function(){doCheckPopupBlocked(poppedWindow);}, 5000);
}

function doCheckPopupBlocked(poppedWindow) {

    var result = false;

    try {
        if (typeof poppedWindow == 'undefined') {
            // Safari with popup blocker... leaves the popup window handle undefined
            result = true;
        }
        else if (poppedWindow && poppedWindow.closed) {
            // This happens if the user opens and closes the client window...
            // Confusing because the handle is still available, but it's in a "closed" state.
            // We're not saying that the window is not being blocked, we're just saying
            // that the window has been closed before the test could be run.
            result = false;
        }
        else if (poppedWindow && poppedWindow.outerWidth == 0) {
            // This is usually Chrome's doing. The outerWidth (and most other size/location info)
         // will be left at 0, EVEN THOUGH the contents of the popup will exist (including the
         // test function we check for next). The outerWidth starts as 0, so a sufficient delay
         // after attempting to pop is needed.
            result = true;
        }
        else if (poppedWindow && poppedWindow.test) {
            // This is the actual test. The client window should be fine.
            result = false;
        }
        else {
            // Else we'll assume the window is not OK
            result = true;
        }

    } catch (err) {
        //if (console) {
        //    console.warn("Could not access popup window", err);
        //}
    }

    if(result)
     alert("The popup was blocked. You must allow popups to use this site.");
}

Para usarlo solo haz esto:

var popup=window.open('location',etc...);
checkPopupBlocked(popup);

Si la ventana emergente se bloquea, el mensaje de alerta se mostrará después del período de gracia de 5 segundos (puede ajustar eso, pero 5 segundos deberían ser bastante seguros).

Kandelon
fuente
2

Este fragmento incorpora todo lo anterior. Por alguna razón, StackOverflow excluye la primera y la última línea de código en el bloque de código a continuación, así que escribí un blog sobre él. Para obtener una explicación completa y el resto del código (descargable), consulte mi blog en thecodeabode.blogspot.com

var PopupWarning = {

    init : function()
    {

        if(this.popups_are_disabled() == true)
        {
            this.redirect_to_instruction_page();
        }
    },

    redirect_to_instruction_page : function()
    {
        document.location.href = "http://thecodeabode.blogspot.com";
    },

    popups_are_disabled : function()
    {
        var popup = window.open("http://localhost/popup_with_chrome_js.html", "popup_tester", "width=1,height=1,left=0,top=0");

        if(!popup || popup.closed || typeof popup == 'undefined' || typeof popup.closed=='undefined')
        {
            return true;
        }

        window.focus();
        popup.blur();

        //
        // Chrome popup detection requires that the popup validates itself - so we need to give
        // the popup time to load, then call js on the popup itself
        //
        if(navigator && (navigator.userAgent.toLowerCase()).indexOf("chrome") > -1)
        {
            var on_load_test = function(){PopupWarning.test_chrome_popups(popup);};     
            var timer = setTimeout(on_load_test, 60);
            return;
        }


        popup.close();
        return false;
    },

    test_chrome_popups : function(popup)
    {
        if(popup && popup.chrome_popups_permitted && popup.chrome_popups_permitted() == true)
        {
            popup.close();
            return true;
        }

        //
        // If the popup js fails - popups are blocked
        //
        this.redirect_to_instruction_page();
    }
};

PopupWarning.init();
Ben
fuente
2

Vaya, seguro que hay muchas soluciones aquí. Esto es mío, utiliza soluciones tomadas de la respuesta aceptada actual (que no funciona en la última versión de Chrome y requiere envolverla en un tiempo de espera), así como una solución relacionada en este hilo (que en realidad es Vanilla JS, no jQuery) .

El mío usa una arquitectura de devolución de llamada que se enviará truecuando la ventana emergente esté bloqueada y de lo falsecontrario.

window.isPopupBlocked = function(popup_window, cb)
{
    var CHROME_CHECK_TIME = 2000;       // the only way to detect this in Chrome is to wait a bit and see if the window is present

    function _is_popup_blocked(popup)
    {
        return !popup.innerHeight;
    }

    if (popup_window) {
        if (popup_window.closed) {
            // opened OK but was closed before we checked
            cb(false);
            return;
        }
        if (/chrome/.test(navigator.userAgent.toLowerCase())) {
            // wait a bit before testing the popup in chrome
            setTimeout(function() {
                cb(_is_popup_blocked(popup_window));
            }, CHROME_CHECK_TIME);
        } else {
            // for other browsers, add an onload event and check after that
            popup_window.onload = function() {
                cb(_is_popup_blocked(popup_window));
            };
        }
    } else {
        cb(true);
    }
};
pospi
fuente
1

La respuesta de Jason es el único método en el que puedo pensar también, ¡pero confiar en una posición como esa es un poco arriesgado!

En estos días, realmente no necesita hacer la pregunta "¿se bloqueó mi ventana emergente no solicitada?", Porque la respuesta es invariablemente "sí": todos los navegadores principales tienen el bloqueador de ventanas emergentes activado de forma predeterminada. El mejor enfoque es solo window.open () en respuesta a un clic directo, que casi siempre está permitido.

bobince
fuente
2
Conozco las mejores prácticas, etc. Pero me encuentro en una situación en la que necesito realizar esta tarea. Por eso hice esta pregunta y no "¿debería?"
Andrew Ensley
1

HOLA

Modifiqué ligeramente las soluciones descritas anteriormente y creo que al menos funciona para Chrome. Mi solución está hecha para detectar si la ventana emergente está bloqueada cuando se abre la página principal, no cuando se abre la ventana emergente, pero estoy seguro de que hay algunas personas que pueden modificarla. :-) El inconveniente aquí es que se muestra la ventana emergente durante un par de segundos (podría ser posible acortar un poco) cuando no hay un bloqueador de ventanas emergentes.

Pongo esto en la sección de mi ventana 'principal'

<script type="text/JavaScript" language="JavaScript">

 var mine = window.open('popuptest.htm','popuptest','width=1px,height=1px,left=0,top=0,scrollbars=no');
 if(!mine|| mine.closed || typeof mine.closed=='undefined')
  {
    popUpsBlocked = true       
    alert('Popup blocker detected ');
    if(mine)
      mine.close();
 }
 else
 {
    popUpsBlocked = false    
    var cookieCheckTimer = null;
    cookieCheckTimer =  setTimeout('testPopup();', 3500);
 }


function testPopup()
{
  if(mine)
  {
    if(mine.test())
    {
       popUpsBlocked = false;
    }
    else
    {
        alert('Popup blocker detected ');
         popUpsBlocked = true;
     }
    mine.close();
}

} 
</script>

El popuptest se ve así:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
    <title>Popup test</title>
<script type="text/javascript" language="Javascript">
   function test() {if(window.innerHeight!=0){return true;} else return false;}
</script>
</head>

<body>
</body>
</html>

Cuando llamo a la función de prueba en la página emergente después de 3500 ms, Chrome ha configurado correctamente la altura interna.

Utilizo la variable popUpsBlocked para saber si las ventanas emergentes se muestran o no en otros javascripts. es decir

function ShowConfirmationMessage()
{
if(popUpsBlocked)
 { 
  alert('Popups are blocked, can not display confirmation popup. A mail will be sent with the confirmation.');
 } 
 else
 { 
  displayConfirmationPopup();
 }
 mailConfirmation();
}
Lars
fuente
Desafortunadamente, esto supone que la página que está intentando abrir está controlada por nosotros. Necesito abrir una página externa sobre la que no tengo control.
Roman
1
function openPopUpWindow(format)
{   
    var win = window.open('popupShow.html',
                          'ReportViewer',
                          'width=920px,height=720px,left=50px,top=20px,location=no,directories=no,status=no,menubar=no,toolbar=no,resizable=1,maximize:yes,scrollbars=0');

    if (win == null || typeof(win) == "undefined" || (win == null && win.outerWidth == 0) || (win != null && win.outerHeight == 0) || win.test == "undefined") 
    {
        alert("The popup was blocked. You must allow popups to use this site.");  
    }
    else if (win)
    {
        win.onload = function()
        {          
            if (win.screenX === 0) {
                alert("The popup was blocked. You must allow popups to use this site.");
                win.close();
            } 
        };
    }
}
syed
fuente
0

Por lo que puedo decir (por lo que he probado), Chrome devuelve un objeto de ventana con la ubicación "about: blank". Entonces, lo siguiente debería funcionar para todos los navegadores:

var newWin = window.open(url);
if(!newWin || newWin.closed || typeof newWin.closed=='undefined' || newWin.location=='about:blank')
{
    //POPUP BLOCKED
}
Yoav Aharoni
fuente
la ubicación seguirá siendo "about: blank" incluso para las ventanas emergentes que no estén bloqueadas. Probé en Chrome v28.0.1500.72
Roman