Detectar clique no Iframe usando JavaScript


128

Entendo que não é possível dizer o que o usuário está fazendo dentro de um iframese for um domínio cruzado. O que eu gostaria de fazer é acompanhar se o usuário clicou em tudo no iframe. Eu imagino um cenário em que há um invisível divem cima do iframee o e diventão passará o evento de clique para o iframe.

É algo assim possível? Se for, então como eu faria isso? Os iframesanúncios são, portanto, não tenho controle sobre as tags usadas.


4
É possível e não há solução crossbrowser: stackoverflow.com/a/32138108/1064513
Dmitry Kochin

Respostas:


39

É algo assim possível?

Não. Tudo o que você pode fazer é detectar o mouse entrando no iframe e potencialmente (embora não seja confiável) quando ele voltar (ou seja, tentar descobrir a diferença entre o ponteiro que passa pelo anúncio a caminho de outro lugar e o restante) no anúncio).

Eu imagino um cenário em que há uma div invisível no topo do iframe e a div passará o evento click para o iframe.

Não, não há como falsificar um evento de clique.

Ao capturar a ratoeira, você impediria que o clique original chegasse ao iframe. Se você pudesse determinar quando o botão do mouse estava prestes a ser pressionado, poderia tentar tirar o div invisível do caminho para que o clique passasse ... mas também não há evento que seja acionado imediatamente antes da queda do mouse.

Você pode tentar adivinhar, por exemplo, olhando para ver se o ponteiro parou, supondo que um clique esteja prestes a chegar. Mas é totalmente não confiável e, se você falhar, acaba de perder um clique.


4
Sim, ele é. E não há solução crossbrowser: stackoverflow.com/a/32138108/1064513
Dmitry Kochin

1
Verifiquei esses links e acho que a resposta está correta. Você pode detectar apenas um clique dentro do iframe, mas não o que foi clicado.
user568021

Eu voto negativo, apenas porque não é totalmente verdade. A maior parte do que você está dizendo é verdadeira, mas existem soluções alternativas, como é a resposta mais popular neste segmento.
newms87

154

Isso é certamente possível. Isso funciona no Chrome, Firefox e IE 11 (e provavelmente outros).

focus();
var listener = window.addEventListener('blur', function() {
    if (document.activeElement === document.getElementById('iframe')) {
        // clicked
    }
    window.removeEventListener('blur', listener);
});

JSFiddle


Advertência: Isso detecta apenas o primeiro clique. Pelo que entendi, é tudo o que você deseja.


1
@the_joric, é porque foram 4 anos após a pergunta e as pessoas normalmente não passam as primeiras respostas.
Paul Draper

3
Também é importante notar que, se você alterar as guias do navegador, ele disparará o foco ();
Linnay

7
NÃO funciona no Firefox. O JSFiddle contém erros que ocultam isso: = em vez de ===. Há solução crossbrowser (mesmo no IE8): stackoverflow.com/a/32138108/1064513
Dmitry Kochin

8
O evento de desfoque não é acionado se o usuário não clicar no documento principal primeiro! Além disso, isso não é útil para detectar cliques em vários iframes, pois não há evento que seja acionado quando o foco for alterado de um iframe para outro (o blurevento do iframe não é acionado ).
Tomáš Kafka

1
por que existe dependência de focus ();
Prasad Shinde

107

Com base na resposta de Mohammed Radwan, vim com a seguinte solução jQuery. Basicamente, o que faz é acompanhar o que as pessoas do iFrame estão pairando. Então, se a janela ficar embaçada, provavelmente significa que o usuário clicou no banner iframe.

o iframe deve ser colocado em uma div com um ID, para garantir que você saiba em qual iframe o usuário clicou:

<div class='banner' bannerid='yyy'>
    <iframe src='http://somedomain.com/whatever.html'></iframe>
<div>

tão:

$(document).ready( function() {
    var overiFrame = -1;
    $('iframe').hover( function() {
        overiFrame = $(this).closest('.banner').attr('bannerid');
    }, function() {
        overiFrame = -1
    });

... isso mantém o overiFrame em -1 quando nenhum iFrames é pairado ou o 'bannerid' definido na divisão de quebra automática quando um iframe é pairado. Tudo o que você precisa fazer é verificar se 'overiFrame' está definido quando a janela fica embaçada, assim: ...

    $(window).blur( function() {
        if( overiFrame != -1 )
            $.post('log.php', {id:overiFrame}); /* example, do your stats here */
    });
});

Solução muito elegante com uma desvantagem menor: se um usuário pressionar ALT-F4 ao passar o mouse sobre um iFrame, ele o registrará como um clique. Porém, isso só aconteceu no FireFox, IE, Chrome e Safari não o registraram.

Mais uma vez obrigado Mohammed, solução muito útil!


Eu marquei esta resposta com +1, embora tenha os seguintes problemas: 1. Quando existem vários iframes, você clica em um deles e imediatamente no outro - o segundo clique não é detectado. 2. Vários cliques dentro de um iframe também não são contados. 3. Não funciona corretamente no celular, porque você não pode fazer o evento "pairar" com um dedo.
Sych

O script acima é usado por mim para detectar cliques fora do meu site. A maioria das redes de publicidade agora exibe banners em frames. Se você clicar em um e depois rapidamente em outro antes de sair no primeiro clique, tecnicamente quero saber o último clique em que você realmente deixou. Então, no meu caso, é um comportamento desejado. Também é bom detectar cliques em banners para celular. Assim, o foco deve ser lançado logo antes do clique é executado
patrick

Não está funcionando no caso de elementos svg no conteúdo iframe :( stackoverflow.com/questions/32589735/…
Serhiy 15/15

@Serhiy, isso é porque você não está realmente sair da página original ao clicar no iframe ...
patrick

6
Essa resposta é a melhor delas, no entanto, se você deseja receber todos os cliques no iframe, é necessário focá-lo assim que o usuário clicar para monitorar outros cliques. Isto deve ser adicionado à seção $ (janela) .blur (): setTimeout(function(){ window.focus(); }, 0);. Agora, o usuário clica, coloca o foco no iframe, o script retira esse foco e agora pode monitorar outras alterações de foco de cliques futuros.
precisa saber é o seguinte

89

Esta é uma solução pequena que funciona em todos os navegadores, mesmo no IE8:

var monitor = setInterval(function(){
    var elem = document.activeElement;
    if(elem && elem.tagName == 'IFRAME'){
        clearInterval(monitor);
        alert('clicked!');
    }
}, 100);

Você pode testá-lo aqui: http://jsfiddle.net/oqjgzsm0/


1
E se você tiver vários iframes e não souber o ID deles?
shankshera

1
única solução confiável entre navegadores que também funciona na versão mais recente do FF! Muito obrigado. Merece mais
votos positivos

6
@shankshera É só pegar o elem.id, esse é o seu iframe id :). Veja jsfiddle.net/oqjgzsm0/219
Tomáš Kafka

1
Estou usando isso para rastrear cliques em botões sociais. Mas como 3/4 dos que estou usando usam iframes, preciso rastrear cliques em vários iframes. Atualizei o violino para permitir isso: jsfiddle.net/oqjgzsm0/273 . Ele define um novo intervalo que verifica se um clique está fora do último iframe clicado. Em seguida, redefine o intervalo original para verificar se há cliques novamente. Ele não rastreia vários cliques no mesmo iframe sem um clique fora dele.
Brouxhaha 07/04

14
Além do fato de que o uso de um intervalo de loop contínuo a uma taxa não é uma idéia muito boa, isso vai detectar falsos positivos se o foco set usuário no iframe através da navegação tecla tab
Kaiido

36

O código a seguir mostrará se o usuário clicar / passar o mouse ou sair do iframe: -

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Detect IFrame Clicks</title>
<script type="text/javascript">
    $(document).ready(function() {
        var isOverIFrame = false;

        function processMouseOut() {
            log("IFrame mouse >> OUT << detected.");
            isOverIFrame = false;
            top.focus();
        }

        function processMouseOver() {
            log("IFrame mouse >> OVER << detected.");
            isOverIFrame = true;
        }

        function processIFrameClick() {
            if(isOverIFrame) {
                // replace with your function
                log("IFrame >> CLICK << detected. ");
            }
        }

        function log(message) {
            var console = document.getElementById("console");
            var text = console.value;
            text = text + message + "\n";
            console.value = text;
        }

        function attachOnloadEvent(func, obj) {
            if(typeof window.addEventListener != 'undefined') {
                window.addEventListener('load', func, false);
            } else if (typeof document.addEventListener != 'undefined') {
                document.addEventListener('load', func, false);
            } else if (typeof window.attachEvent != 'undefined') {
                window.attachEvent('onload', func);
            } else {
                if (typeof window.onload == 'function') {
                    var oldonload = onload;
                    window.onload = function() {
                        oldonload();
                        func();
                    };
                } else {
                    window.onload = func;
                }
            }
        }

        function init() {
            var element = document.getElementsByTagName("iframe");
            for (var i=0; i<element.length; i++) {
                element[i].onmouseover = processMouseOver;
                element[i].onmouseout = processMouseOut;
            }
            if (typeof window.attachEvent != 'undefined') {
                top.attachEvent('onblur', processIFrameClick);
            }
            else if (typeof window.addEventListener != 'undefined') {
                top.addEventListener('blur', processIFrameClick, false);
            }
        }

        attachOnloadEvent(init);
    });
</script>
</head>
<body>
<iframe src="www.google.com" width="100%" height="1300px"></iframe>
<br></br>
<br></br>
<form name="form" id="form" action=""><textarea name="console"
id="console" style="width: 100%; height: 300px;" cols="" rows=""></textarea>
<button name="clear" id="clear" type="reset">Clear</button>
</form>
</body>
</html>

Você precisa substituir o src no iframe pelo seu próprio link. Espero que isso ajude. Atenciosamente, Mo.


1
Com base em testes rápidos, o exemplo fornecido (após corrigir o URL) parece funcionar no IE 8, de maneira confiável no Chrome 14.0.835.186 m, mas não no Firefox 6.0.2.
Matthew Flaschen

Funciona bem para o Chrome, mas não funciona para Firefox V62, porque quando clique em iframe borrão evento não é jogado
slesh

11

Acabei de encontrar esta solução ... Eu tentei, adorei ..

Funciona para iframes entre domínios para desktop e celular!

Ainda não sei se é à prova de falhas

window.addEventListener('blur',function(){
      if(document.activeElement.id == 'CrossDomainiframeId'){
        //do something :-)
      }
});

Feliz codificação


2
Esta mesma resposta (talvez uma versão ligeiramente melhor) foi publicado um ano antes aqui na mesma página: stackoverflow.com/a/23231136/470749
Ryan

5

Você pode conseguir isso usando o evento blur no elemento window.

Aqui está um plugin jQuery para rastrear cliques em iframes (ele acionará uma função de retorno de chamada personalizada quando um iframe for clicado): https://github.com/finalclap/iframeTracker-jquery

Use-o assim:

jQuery(document).ready(function($){
    $('.iframe_wrap iframe').iframeTracker({
        blurCallback: function(){
            // Do something when iframe is clicked (like firing an XHR request)
        }
    });
});

5

consulte http://jsfiddle.net/Lcy797h2/ para obter minha solução prolongada que não funciona de maneira confiável no IE

        $(window).on('blur',function(e) {    
            if($(this).data('mouseIn') != 'yes')return;
            $('iframe').filter(function(){
                return $(this).data('mouseIn') == 'yes';
            }).trigger('iframeclick');    
        });

        $(window).mouseenter(function(){
            $(this).data('mouseIn', 'yes');
        }).mouseleave(function(){
            $(this).data('mouseIn', 'no');
        });

        $('iframe').mouseenter(function(){
            $(this).data('mouseIn', 'yes');
            $(window).data('mouseIn', 'yes');
        }).mouseleave(function(){
            $(this).data('mouseIn', null);
        });

        $('iframe').on('iframeclick', function(){
            console.log('Clicked inside iframe');
            $('#result').text('Clicked inside iframe'); 
        });
        $(window).on('click', function(){
            console.log('Clicked inside window');
            $('#result').text('Clicked inside window'); 
        }).blur(function(){
            console.log('window blur');
        });

        $('<input type="text" style="position:absolute;opacity:0;height:0px;width:0px;"/>').appendTo(document.body).blur(function(){
                $(window).trigger('blur');
            }).focus();

Seu homem incrível de codificação .... o que realmente eu quero ... + 1 a @Omar Jackman .. muito útil para a captura, clique no youtube anúncio
saun4frsh

4

Isso funciona para mim em todos os navegadores (incluído no Firefox)

https://gist.github.com/jaydson/1780598

https://jsfiddle.net/sidanmor/v6m9exsw/

var myConfObj = {
  iframeMouseOver : false
}
window.addEventListener('blur',function(){
  if(myConfObj.iframeMouseOver){
    console.log('Wow! Iframe Click!');
  }
});

document.getElementById('idanmorblog').addEventListener('mouseover',function(){
   myConfObj.iframeMouseOver = true;
});
document.getElementById('idanmorblog').addEventListener('mouseout',function(){
    myConfObj.iframeMouseOver = false;
});
<iframe id="idanmorblog" src="https://sidanmor.com/" style="width:400px;height:600px" ></iframe>

<iframe id="idanmorblog" src="https://sidanmor.com/" style="width:400px;height:600px" ></iframe>


3

Mohammed Radwan, sua solução é elegante. Para detectar cliques em iframe no Firefox e IE, você pode usar um método simples com document.activeElement e um timer, no entanto ... Pesquisei nas interwebs um método para detectar cliques em um iframe no Chrome e Safari. À beira de desistir, acho sua resposta. Obrigado senhor!

Algumas dicas: Eu achei sua solução mais confiável ao chamar a função init () diretamente, e não através de attachOnloadEvent (). Obviamente, para fazer isso, você deve chamar init () somente após o iframe html. Então, seria algo como:

<script>
var isOverIFrame = false;
function processMouseOut() {
    isOverIFrame = false;
    top.focus();
}
function processMouseOver() { isOverIFrame = true; }
function processIFrameClick() {
    if(isOverIFrame) {
    //was clicked
    }
}

function init() {
    var element = document.getElementsByTagName("iframe");
    for (var i=0; i<element.length; i++) {
        element[i].onmouseover = processMouseOver;
        element[i].onmouseout = processMouseOut;
    }
    if (typeof window.attachEvent != 'undefined') {
        top.attachEvent('onblur', processIFrameClick);
    }
    else if (typeof window.addEventListener != 'undefined') {
        top.addEventListener('blur', processIFrameClick, false);
    }
}
</script>

<iframe src="http://google.com"></iframe>

<script>init();</script>

3

Você pode fazer isso para fazer bolhas de eventos no documento pai:

$('iframe').load(function() {
    var eventlist = 'click dblclick \
                    blur focus focusin focusout \
                    keydown keypress keyup \
                    mousedown mouseenter mouseleave mousemove mouseover mouseout mouseup mousemove \
                    touchstart touchend touchcancel touchleave touchmove';

    var iframe = $('iframe').contents().find('html');

    // Bubble events to parent
    iframe.on(eventlist, function(event) {
        $('html').trigger(event);
    });
});

Apenas estenda a lista de eventos para mais eventos.


Eu usei o evento 'touchend' e funcionou! Sua resposta me ajudou muito!

3

Eu tive uma situação em que tinha que rastrear cliques em um botão de mídia social puxado por um iframe. Uma nova janela seria aberta quando o botão fosse clicado. Aqui estava a minha solução:

var iframeClick = function () {
    var isOverIframe = false,
    windowLostBlur = function () {
        if (isOverIframe === true) {
            // DO STUFF
            isOverIframe = false;
        }
    };
    jQuery(window).focus();
    jQuery('#iframe').mouseenter(function(){
        isOverIframe = true;
        console.log(isOverIframe);
    });
    jQuery('#iframe').mouseleave(function(){
        isOverIframe = false;
        console.log(isOverIframe);
    });
    jQuery(window).blur(function () {
        windowLostBlur();
    });
};
iframeClick();

3

http://jsfiddle.net/QcAee/406/

Basta criar uma camada invisível sobre o iframe que voltará quando clicar e subir quando o evento Mouseleave for disparado!
Precisa de jQuery

esta solução não propaga primeiro clique dentro do iframe!

$("#invisible_layer").on("click",function(){
		alert("click");
		$("#invisible_layer").css("z-index",-11);

});
$("iframe").on("mouseleave",function(){
		$("#invisible_layer").css("z-index",11);
});
iframe {
    width: 500px;
    height: 300px;
}
#invisible_layer{
  position: absolute;
  background-color:trasparent;
  width: 500px;
  height:300px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="message"></div>
<div id="invisible_layer">

</div>
<iframe id="iframe" src="//example.com"></iframe>


1

Isso definitivamente funciona se o iframe for do mesmo domínio que o site pai. Não testei para sites entre domínios.

$(window.frames['YouriFrameId']).click(function(event){  /* do something here  */ });
$(window.frames['YouriFrameId']).mousedown(function(event){ /* do something here */ });
$(window.frames['YouriFrameId']).mouseup(function(event){ /* do something here */ });

Sem o jQuery, você poderia tentar algo assim, mas novamente eu não tentei isso.

window.frames['YouriFrameId'].onmousedown = function() { do something here }

Você pode até filtrar seus resultados:

$(window.frames['YouriFrameId']).mousedown(function(event){   
  var eventId = $(event.target).attr('id');      
  if (eventId == 'the-id-you-want') {
   //  do something
  }
});

1

A combinação da resposta acima com a capacidade de clicar repetidamente sem clicar fora do iframe.

    var eventListener = window.addEventListener('blur', function() {
    if (document.activeElement === document.getElementById('contentIFrame')) {
        toFunction(); //function you want to call on click
        setTimeout(function(){ window.focus(); }, 0);
    }
    window.removeEventListener('blur', eventListener );
    });

1

Podemos capturar todos os cliques. A idéia é redefinir o foco em um elemento fora do iFrame após cada clique:

    <input type="text" style="position:fixed;top:-1000px;left:-1000px">
    <div id="message"></div>
    <iframe id="iframe" src="//example.com"></iframe>
    <script>
        focus();
        addEventListener('blur', function() {
            if(document.activeElement = document.getElementById('iframe')) {
                message.innerHTML += 'Clicked';
                setTimeout(function () {
                    document.querySelector("input").focus();
                    message.innerHTML += ' - Reset focus,';
                }, 1000);
            }  
        });
    </script>

JSFiddle


0

Eu acredito que você pode fazer algo como:

$('iframe').contents().click(function(){function to record click here });

usando jQuery para fazer isso.


0

Como encontrado lá: Detectar o Click no Iframe usando JavaScript

=> Podemos usar iframeTracker-jquery :

$('.carousel-inner .item').each(function(e) {
    var item = this;
    var iFrame = $(item).find('iframe');
    if (iFrame.length > 0) {
        iFrame.iframeTracker({
            blurCallback: function(){
                // Do something when iFrame is clicked (like firing an XHR request)
                onItemClick.bind(item)(); // calling regular click with right context
                console.log('IFrameClick => OK');
            }
        });
        console.log('IFrameTrackingRegistred => OK');
    }
})

0

Com base na resposta de Paul Draper, criei uma solução que funciona continuamente quando você tem Iframes que abrem outra guia no navegador. Quando você retorna a página, continua ativa para detectar o clique na estrutura, esta é uma situação muito comum:

          focus();
        $(window).blur(() => {
           let frame = document.activeElement;
           if (document.activeElement.tagName == "IFRAME") {
             // Do you action.. here  frame has the iframe clicked
              let frameid = frame.getAttribute('id')
              let frameurl = (frame.getAttribute('src'));
           }            
        });

        document.addEventListener("visibilitychange", function () {
            if (document.hidden) {

            } else {
                focus();
            }
        });

O código é simples, o evento de desfoque detecta a perda de foco quando o iframe é clicado e testa se o elemento ativo é o iframe (se você tiver vários iframe para saber quem foi selecionado) essa situação é frequente quando você tem quadros de publicidade .

O segundo evento aciona um método de foco quando você retorna à página. é usado o evento de alteração de visibilidade.


0

Aqui está a solução usando abordagens sugeridas com hover + blur e truques de elementos ativos, não bibliotecas, apenas js puros. Funciona bem para FF / Chrome. Principalmente a abordagem é igual à proposta por @Mohammed Radwan, exceto pelo uso de um método diferente proposto por @ zone117x para rastrear o clique do iframe no FF, porque o window.focus não está funcionando sem as configurações adicionais do usuário :

Faz um pedido para trazer a janela para a frente. Pode falhar devido às configurações do usuário e não é garantido que a janela esteja na frente antes que esse método retorne.

Aqui está o método composto:

function () {
    const state = {};

    (function (setup) {
        if (typeof window.addEventListener !== 'undefined') {
            window.addEventListener('load', setup, false);
        } else if (typeof document.addEventListener !== 'undefined') {
            document.addEventListener('load', setup, false);
        } else if (typeof window.attachEvent !== 'undefined') {
            window.attachEvent('onload', setup);
        } else {
            if (typeof window.onload === 'function') {
                const oldonload = onload;
                window.onload = function () {
                    oldonload();
                    setup();
                };
            } else {
                window.onload = setup;
            }
        }
    })(function () {
        state.isOverIFrame = false;
        state.firstBlur = false;
        state.hasFocusAcquired = false;

        findIFramesAndBindListeners();

        document.body.addEventListener('click', onClick);

        if (typeof window.attachEvent !== 'undefined') {
            top.attachEvent('onblur', function () {
                state.firstBlur = true;
                state.hasFocusAcquired = false;
                onIFrameClick()
            });
            top.attachEvent('onfocus', function () {
                state.hasFocusAcquired = true;
                console.log('attachEvent.focus');
            });
        } else if (typeof window.addEventListener !== 'undefined') {
            top.addEventListener('blur', function () {
                state.firstBlur = true;
                state.hasFocusAcquired = false;
                onIFrameClick();
            }, false);
            top.addEventListener('focus', function () {
                state.hasFocusAcquired = true;
                console.log('addEventListener.focus');
            });
        }

        setInterval(findIFramesAndBindListeners, 500);
    });

    function isFF() {
        return navigator.userAgent.search(/firefox/i) !== -1;
    }

    function isActiveElementChanged() {
        const prevActiveTag = document.activeElement.tagName.toUpperCase();
        document.activeElement.blur();
        const currActiveTag = document.activeElement.tagName.toUpperCase();
        return !prevActiveTag.includes('BODY') && currActiveTag.includes('BODY');
    }

    function onMouseOut() {
        if (!state.firstBlur && isFF() && isActiveElementChanged()) {
            console.log('firefox first click');
            onClick();
        } else {
            document.activeElement.blur();
            top.focus();
        }
        state.isOverIFrame = false;
        console.log(`onMouseOut`);
    }

    function onMouseOver() {
        state.isOverIFrame = true;
        console.log(`onMouseOver`);
    }

    function onIFrameClick() {
        console.log(`onIFrameClick`);
        if (state.isOverIFrame) {
            onClick();
        }
    }

    function onClick() {
        console.log(`onClick`);
    }

    function findIFramesAndBindListeners() {
        return Array.from(document.getElementsByTagName('iframe'))
            .forEach(function (element) {
                element.onmouseover = onMouseOver;
                element.onmouseout = onMouseOut;
            });
    }
}

0

Pressupostos -

  1. Seu script é executado fora do iframe, MAS NÃO na janela window.top mais externa. (Para a janela mais externa, outras soluções de desfoque são boas o suficiente)
  2. Uma nova página é aberta substituindo a página atual / uma nova página em uma nova guia e o controle é alternado para a nova guia.

Isso funciona para iframes de fontes e sem fontes

var ifr = document.getElementById("my-iframe");
var isMouseIn;
ifr.addEventListener('mouseenter', () => {
    isMouseIn = true;
});
ifr.addEventListener('mouseleave', () => {
    isMouseIn = false;
});
window.document.addEventListener("visibilitychange", () => {
    if (isMouseIn && document.hidden) {
        console.log("Click Recorded By Visibility Change");
    }
});
window.addEventListener("beforeunload", (event) => {
    if (isMouseIn) {
        console.log("Click Recorded By Before Unload");
    }
});

Se uma nova guia for aberta / a mesma página descarregar e o ponteiro do mouse estiver dentro do Iframe, um clique será considerado

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.