Detectar pop-up bloqueado no Chrome

103

Estou ciente das técnicas de javascript para detectar se um pop-up está bloqueado em outros navegadores (conforme descrito na resposta a esta pergunta ). Aqui está o teste básico:

var newWin = window.open(url);

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

Mas isso não funciona no Chrome. A seção "POPUP BLOCKED" nunca é alcançada quando o popup é bloqueado.

Claro, o teste está funcionando até certo ponto, já que o Chrome não bloqueia o pop-up, mas o abre em uma pequena janela minimizada no canto inferior direito que lista os pop-ups "bloqueados".

O que eu gostaria de fazer é saber se o popup foi bloqueado pelo bloqueador de popup do Chrome. Tento evitar o farejamento do navegador em favor da detecção de recursos. Existe uma maneira de fazer isso sem farejar o navegador?

Editar : Eu já tentei fazer uso de newWin.outerHeight, newWin.lefte outras propriedades semelhantes para alcançar este objetivo. O Google Chrome retorna todos os valores de posição e altura como 0 quando o pop-up é bloqueado.

Infelizmente, ele também retorna os mesmos valores, mesmo se o pop-up for realmente aberto por um período de tempo desconhecido. Após algum período mágico (alguns segundos no meu teste), as informações de localização e tamanho são retornadas como os valores corretos. Em outras palavras, ainda não estou mais perto de descobrir isso. Qualquer ajuda seria apreciada.

Andrew Ensley
fonte
Yoav, o local mostra o mesmo independentemente de o pop-up estar bloqueado ou não. Alguém mais tem uma resposta que não envolva fazer o usuário esperar 3,5 segundos?
As soluções mais recentes de InvisibleBacon e Andy não funcionam no Chrome 10: a mensagem "falhou para o cromo" aparece mesmo se o pop-up de teste foi exibido com sucesso. Qualquer ideia?
Eu acho que uma nova questão seria apropriada, já que algumas dessas soluções parecem ter funcionado apenas com as primeiras versões do Chrome.
Bryan Field
1
@George Bailey Eu concordo, mas só para ficar claro, alguns deles funcionam na versão atual do Chrome (19). A ideia original de Andrew de usar outerHeight (ou screenX, como outros sugeriram) está funcionando bem para mim, combinada com a abordagem setTimeout. Mas, sim, tentar entender todas essas respostas era realmente confuso até que fiz meu próprio teste.
regular de

Respostas:

66

Bem, o "momento mágico" de que você fala é provavelmente quando o DOM do pop-up foi carregado. Ou então pode ser quando tudo (imagens, CSS externo, etc.) tiver sido carregado. Você pode testar isso facilmente adicionando um gráfico muito grande ao pop-up (limpe o cache primeiro!). Se você estava usando um Javascript Framework como jQuery (ou algo semelhante), você poderia usar o evento ready () (ou algo semelhante) para esperar o DOM carregar antes de verificar o deslocamento da janela. O perigo disso é que a detecção do Safari funciona de forma conflitante: o DOM do pop-up nunca estará pronto () no Safari porque ele fornecerá um identificador válido para a janela que você está tentando abrir - se ela realmente abre ou não. (na verdade, acredito que o código de teste pop-up acima não funcionará no Safari.)

Acho que a melhor coisa que você pode fazer é envolver seu teste em um setTimeout () e dar ao pop-up 3-5 segundos para completar o carregamento antes de executar o teste. Não é perfeito, mas deve funcionar pelo menos 95% do tempo.

Aqui está o código que uso para detecção de navegadores cruzados, sem a parte do 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;
}

O que faço é executar este teste do pai e envolvê-lo em um setTimeout (), dando à janela filho 3-5 segundos para carregar. Na janela filha, você precisa adicionar uma função de teste:

teste de funcionamento() {}

O detector do bloqueador de pop-up testa para ver se a função "teste" existe como um membro da janela filho.

ADICIONADO EM 15 DE JUNHO DE 2015:

Acho que a maneira moderna de lidar com isso seria usar window.postMessage () para que a criança notifique o pai de que a janela foi carregada. A abordagem é semelhante (a criança diz aos pais que está carregada), mas os meios de comunicação melhoraram. Consegui fazer este cross-domain desde a criança:

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

O pai ouve esta mensagem usando:

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

Espero que isto ajude.

Ringo
fonte
Rico, você é um guru do pop-up de javascript. Obrigado. Isso é exatamente o que eu precisava.
Andrew Ensley,
4
Alguma atualização sobre isso? Parece não funcionar mais ... Especificamente no Chrome
Chris Wagner,
Acho que encontrei uma maneira de fazer isso funcionar para novas versões do Chrome. Veja minha resposta para detalhes.
InvisibleBacon
2
Basicamente, há um bug no Chrome. Embora oculte o pop-up, ele ainda executa e você ainda recebe o objeto janela de volta - portanto, as verificações regulares não funcionam. Aqui está a solução que funcionou para mim: var popup = window.open (url); if (popup) {popup.onload = function () {console.log (popup.innerHeight> 0? 'aberto': 'bloqueado'); }} else {console.log ('bloqueado'); } Exemplo de trabalho aqui: jsbin.com/uticev/3
Remy Sharp
1
Esta resposta não está mais correta, altere-a para a resposta de @Predrag Stojadinović
Lucas B
16

Apenas uma melhoria no snipet do InvisibleBacon (testado no IE9, Safari 5, Chrome 9 e 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
fonte
Por que fechar a janela se pop-ups são permitidos? isso não fecharia o pop-up que você queria abrir em primeiro lugar?
elemjay19
3
Usando jQuery, em vez de onload, eu faria $ (myPopup) .ready (). Rodando localmente, meu IE era muito rápido e "onload" já havia ocorrido.
Matt Connolly
12

A seguir está uma solução jQuery para verificação de bloqueador de pop-up. Ele foi testado em FF (v11), Safari (v6), Chrome (v23.0.127.95) e IE (v7 e v9). Atualize a função _displayError para lidar com a mensagem de erro conforme achar necessário.

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 isto ajude! :)

Kevin B
fonte
Isso é realmente útil. Obrigado por compartilhar.
Suvendu Shekhar Giri
Seja bem-vindo, Suvendu, fico feliz que você tenha achado isso útil! Boa codificação! :)
Kevin B
1
Eu ajustei este código para passar dentro / ao redor da url que está tentando ser aberta. Isso permite que o método _displayError () exiba um alerta (estou usando toastr) notificando o usuário de que há um problema e forneça um link clicável que contornará a maioria dos bloqueadores, pois é um link direto. Obrigado por compartilhar !!
Tyler Forsythe
@TylerForsythe você tem mais informações sobre sua solução? Adoraria ser capaz de fornecer um link clicável diretamente para o conteúdo.
Joshua Dance
1
@JoshuaDance Aqui está uma essência que acabei de criar para demonstrar meu código modificado e como o invoco. Espero que ajude! gist.github.com/tylerforsythe/452ceaad62f507d7cb7bd7ddbffe650c
Tyler Forsythe
10

A resposta de Rich não vai funcionar mais para o Chrome. Parece que o Chrome realmente executa qualquer Javascript na janela pop-up agora. Acabei verificando se há um valor de screenX de 0 para verificar se há pop-ups bloqueados. Também acho que encontrei uma maneira de garantir que esta propriedade seja definitiva antes de verificar. Isso só funciona para pop-ups em seu domínio, mas você pode adicionar um gerenciador onload 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 muitos relataram, a propriedade "screenX" às vezes relata diferente de zero para pop-ups com falha, mesmo após onload. Eu também experimentei esse comportamento, mas se você adicionar a verificação após um tempo limite de zero ms, a propriedade screenX sempre parece gerar um valor consistente.

Deixe-me saber se há maneiras de tornar este script mais robusto. Parece funcionar para meus propósitos.

InvisibleBacon
fonte
Não para mim, onloadnunca dispara.
folha de
9

Isso funcionou para mim:

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

OuterHeight e outerWidth são para cromo porque o truque 'about: blank' acima não funciona mais no cromo.

Predrag Stojadinović
fonte
1
Bom entendimento sobre as mudanças do cromo e obrigado por atualizá-lo aqui. Sua resposta deve ser marcada como correta.
Lucas B
O outerWidth e o outerHeight também não funcionam mais no Chrome
Romano
5

Vou apenas copiar / colar a resposta fornecida aqui: https://stackoverflow.com/a/27725432/892099 de DanielB. funciona no Chrome 40 e é muito limpo. nenhum hacks sujo ou envolve espera.

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
fonte
3

Que tal uma Promiseabordagem?

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

E você pode usá-lo como o clássico window.open

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

Você poderia alterar o código para que ele falhasse a promessa em vez de retornar undefined, apenas pensei que ifera um fluxo de controle mais fácil do que try / catchneste caso.

kigiri
fonte
Isso funciona para detectar adblockers de extensão do Chrome. +1
Micheal C Wallas
2

Verifique a posição da janela em relação ao pai. O Chrome faz a janela parecer quase fora da tela.

Jason Cohen
fonte
Vou tentar fazer isso e deixar você saber meus resultados. Obrigado.
Andrew Ensley
O Google Chrome relata os deslocamentos esquerdo e superior como 0 quando o pop-up é "bloqueado". Achei que esse fosse meu bilhete dourado, mas não. Ele relata os deslocamentos como 0 imediatamente após a abertura também. Em algum ponto mágico no futuro após a abertura, os deslocamentos superior e esquerdo são relatados corretamente.
Andrew Ensley,
Verifique meu post para uma maneira que parece garantir que os offsets estão definidos antes de verificar.
InvisibleBacon
2

Eu tive um problema semelhante com pop-ups não abrindo no Chrome. Fiquei frustrado porque não estava tentando fazer algo sorrateiro, como um pop-up onload, apenas abrindo uma janela quando o usuário clicou. Eu estava DUVIDAMENTE frustrado porque executar minha função que incluía o window.open () da linha de comando do firebug funcionou, enquanto clicar no meu link não funcionou! Aqui estava minha solução:

Maneira errada: executando window.open () de um ouvinte de evento (no meu caso, dojo.connect para o método de evento onclick de um nó DOM).

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

Caminho certo: atribuindo uma função à propriedade onclick do nó que chamou window.open ().

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

E, é claro, ainda posso fazer ouvintes de evento para o mesmo evento onclick, se necessário. Com essa mudança, eu poderia abrir minhas janelas mesmo que o Chrome estivesse configurado para "Não permitir que nenhum site mostre pop-ups". Alegria.

Se alguém conhecedor do Chrome puder dizer ao restante de nós por que ele faz a diferença, adoraria ouvir, embora suspeite que seja apenas uma tentativa de fechar a porta para popups programáticos maliciosos.

Chris
fonte
Obrigado por compartilhar sua solução. Funciona. Esta é a maneira melhor e mais limpa de abrir pop-ups cromados. Sua resposta deve estar no topo. O resto das soluções são apenas hacks "sujos".
Mandeep Janjua
2

Esta é uma versão que está funcionando atualmente no Chrome. Apenas uma pequena alteração da solução de Rich, embora eu tenha adicionado um invólucro que controla o tempo também.

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 usá-lo, faça o seguinte:

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

Se o pop-up for bloqueado, a mensagem de alerta será exibida após o período de cortesia de 5 segundos (você pode ajustar isso, mas 5 segundos deve ser bastante seguro).

Kandelon
fonte
2

Este fragmento incorpora todos os itens acima - por algum motivo - StackOverflow está excluindo a primeira e a última linha do código no bloco de código abaixo, então escrevi um blog sobre ele. Para obter uma explicação completa e o restante do código (para download), dê uma olhada em meu blog em 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
fonte
2

Uau, com certeza existem muitas soluções aqui. Este é o meu, ele usa soluções retiradas da resposta aceita atual (que não funciona no Chrome mais recente e requer envolvê-la em um tempo limite), bem como uma solução relacionada neste tópico (que é na verdade JS vanilla, não jQuery) .

O meu usa uma arquitetura de retorno de chamada que será enviada truequando o pop-up for bloqueado e falsecaso contrário.

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
fonte
1

A resposta de Jason é o único método que consigo pensar também, mas confiar em uma posição como essa é um pouco duvidoso!

Atualmente, você realmente não precisa fazer a pergunta “meu popup não solicitado foi bloqueado?”, Porque a resposta é invariavelmente “sim” - todos os principais navegadores têm o bloqueador de popup ativado por padrão. A melhor abordagem é apenas window.open () em resposta a um clique direto, que quase sempre é permitido.

bobince
fonte
2
Eu conheço as melhores práticas, etc. Mas estou em uma situação em que preciso realizar essa tarefa. É por isso que fiz esta pergunta e não "deveria?"
Andrew Ensley,
1

OI

Modifiquei ligeiramente as soluções descritas acima e acho que está funcionando pelo menos para o Chrome. Minha solução é detectar se o pop-up está bloqueado quando a página principal é aberta, não quando o pop-up é aberto, mas tenho certeza que há algumas pessoas que podem modificá-lo. :-) A desvantagem aqui é que a janela pop-up é exibida por alguns segundos (pode ser possível encurtar um pouco) quando não há bloqueador de pop-up.

Eu coloquei isso na seção da minha janela '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>

O popuptest se parece com isto:

<!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>

Como eu chamo a função de teste na página pop-up após 3500 ms, a altura interna foi configurada corretamente pelo Chrome.

Eu uso a variável popUpsBlocked para saber se os pop-ups são exibidos ou não em outros javascripts. ie

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
fonte
Infelizmente, isso pressupõe que a página que você está tentando abrir é controlada por nós. Preciso abrir uma página externa sobre a qual não tenho controle.
Romano
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
fonte
0

Até onde eu posso dizer (pelo que testei), o Chrome retorna um objeto de janela com localização de 'sobre: ​​em branco'. Portanto, o seguinte deve funcionar para todos os navegadores:

var newWin = window.open(url);
if(!newWin || newWin.closed || typeof newWin.closed=='undefined' || newWin.location=='about:blank')
{
    //POPUP BLOCKED
}
Yoav Aharoni
fonte
o local ainda será "about: blank" mesmo para pop-up que não esteja bloqueado. Testei no Chrome v28.0.1500.72
Romano de