使用JavaScript检测点击事件是否发生在iframe中

166

我知道如果iframe是跨域的,就无法判断用户在其中做了什么。我想要做的是追踪用户是否在iframe中点击了任何内容。我设想的情况是在iframe上方有一个不可见的div,然后div将点击事件传递给iframe

这种方法是否可行?如果可行,我该如何实现?这些iframes是广告,所以我无法控制所使用的标签。


4
可以做到,并且有跨浏览器的解决方案:https://dev59.com/AHE95IYBdhLWcg3wRL0t#32138108 - Dmitry Kochin
23个回答

195
这是完全可能的。在Chrome、Firefox和IE 11(以及可能的其他浏览器)中都可以使用。

const message = document.getElementById("message");

// main document must be focused in order for window blur to fire when the iframe is interacted with. 
// There's still an issue that if user interacts outside of the page and then click iframe first without clicking page, the following logic won't run. But since the OP is only concerned about first click this shouldn't be a problem.
window.focus()

window.addEventListener("blur", () => {
  setTimeout(() => {
    if (document.activeElement.tagName === "IFRAME") {
      message.textContent = "clicked " + Date.now();
      console.log("clicked");
    }
  });
}, { once: true });
<div id="message"></div>
<iframe width="50%" height="300" src="//example.com"></iframe>

注意:这只能检测到第一次点击。据我所知,那就是你想要的全部内容。

1
@the_joric,这是因为问题发布已经过去4年了,人们通常不会滚动查看第一批回答之后的内容。 - Paul Draper
4
值得注意的是,如果您切换浏览器选项卡,它会触发focus()函数。 - Linnay
9
它在Firefox中无法运行。JSFiddle包含错误,掩盖了这一点:=而不是===。有跨浏览器解决方案(即使在IE8中也适用):https://dev59.com/AHE95IYBdhLWcg3wRL0t#32138108。 - Dmitry Kochin
12
如果用户没有先单击主文档,模糊事件将不会触发!另外,这种方法不能用于检测对多个 iframe 的单击,因为当焦点从一个 iframe 切换到另一个时,没有触发任何事件(iframe 的“模糊”事件不会触发)。 - Tomáš Kafka
1
为什么在程序中有依赖于focus()的情况? - Prasad Shinde
显示剩余15条评论

121

这是一个小解决方案,在所有浏览器中都可以工作,甚至在IE8中也可以:

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

您可以在这里进行测试:http://jsfiddle.net/oqjgzsm0/


1
如果您有多个iframe,而且您不知道它们的id是什么怎么办? - shankshera
1
只有跨浏览器可靠的解决方案,同时也适用于最新的FF!非常感谢。它值得更多点赞 - BrainOverflow
6
@shankshera,只需获取elem.id,那就是您的iframe id。参见http://jsfiddle.net/oqjgzsm0/219/。 - Tomáš Kafka
4
我使用这个来追踪社交媒体按钮的点击情况。但因为我使用的三分之四按钮使用iframes,所以我需要在多个iframes中跟踪点击。我已经更新了fiddle以实现这一点:http://jsfiddle.net/oqjgzsm0/273/。它设置了一个新的间隔,用于检查是否在上次单击的iframe之外进行了单击。然后重置原始间隔以再次检查点击。如果没有在iframe之外点击,它不会跟踪同一iframe中的多次点击。 - brouxhaha
18
除了使用连续循环间隔不是一个好主意之外,如果用户通过 Tab 键导航设置焦点到 iframe 上,这也会检测到假阳性。 - Kaiido
显示剩余4条评论

113
基于Mohammed Radwan的答案,我想出了以下jQuery解决方案。基本上它所做的是跟踪人们正在悬停的iFrame。然后如果窗口变得模糊,那很可能意味着用户点击了iFrame横幅。
应该将iframe放在一个具有ID的div中,以确保您知道用户单击的是哪个iframe:
<div class='banner' bannerid='yyy'>
    <iframe src='http://somedomain.com/whatever.html'></iframe>
<div>

所以:

$(document).ready( function() {
    var overiFrame = -1;
    $('iframe').hover( function() {
        overiFrame = $(this).closest('.banner').attr('bannerid');
    }, function() {
        overiFrame = -1
    });
这会在没有iFrame被悬停时将overiFrame保持为-1,或者在悬停了iframe时设置在包装div中的“bannerid”。您只需在窗口失去焦点时检查是否设置了“overiFrame”,如下所示:
    $(window).blur( function() {
        if( overiFrame != -1 )
            $.post('log.php', {id:overiFrame}); /* example, do your stats here */
    });
});

非常优雅的解决方案,但有一个小缺陷:如果用户在鼠标悬停在iFrame上时按下ALT-F4,它将被记录为一次点击。这只发生在FireFox中,IE、Chrome和Safari没有注册它。

再次感谢Mohammed,非常有用的解决方案!


我已经点赞了这个答案,但是它存在以下问题:1. 当有多个 iframe 时,您点击其中一个,然后立即点击另一个 - 第二次点击不会被检测到。2. 在 iframe 内部进行多次点击也不会被计算。3. 在移动设备上无法正常工作,因为您无法用手指进行“悬停”事件。 - Sych
上述脚本是我用来检测用户离开我的网站的点击行为。现在大多数广告网络都会在框架中提供横幅广告。如果您在第一次点击后很快地点击了另一个广告,而且在第二次点击之前您还没有离开页面,那么从技术上讲,我想知道您实际上最后离开的是哪个广告。所以在我的情况下,这是期望的行为。它可以很好地检测移动横幅广告的点击行为。因此,必须在执行点击操作之前启动悬停。 - patrick
7
这个答案是最好的,但是,如果你希望在iframe中收到每一个点击事件,你需要在用户点击后将焦点移开,以便监视后续的点击事件。应该在 $(window).blur() 部分添加以下代码:setTimeout(function(){ window.focus(); }, 0);。现在,用户点击时,将焦点置于iframe中,脚本会将焦点拉回,并能够监视未来点击事件的焦点变化。 - HelpingHand
@HelpingHand,该脚本是为实际在点击iframe时加载另一个网站的页面设计的(这在广告中是常见情况)... 你说得对,如果要计算多次点击,需要重置,但为什么要设置超时?只需将'window.focus()'添加到$(window).blur()部分即可解决问题,对吧? - patrick
1
@HelpingHand 用户将失去iframe中的焦点。 想象一下,如果用户正在聚焦iframe中的输入,该方法将使他们失去焦点。 orz.... - xianshenglu
显示剩余4条评论

43

这种情况可能吗?

不可能。你只能检测鼠标进入 iframe 的事件,而且很难(并非可靠地)检测到鼠标离开 iframe 的事件(例如:试图区分光标经过广告然后去其他地方和光标停留在广告上的区别)。

我想象一种场景,在 iframe 上方有一个透明的 div,然后该 div 只是将点击事件传递到 iframe。

不行,无法伪造点击事件。

通过捕获 mousedown 事件,您将阻止原始点击事件传递到 iframe。如果您可以确定鼠标按钮即将被按下的时间点,您可以尝试将无形的 div 移开,以便点击可以穿过...但是没有在 mousedown 之前触发的事件。

您可以尝试猜测,例如通过查看指针是否停止移动,猜测可能会发生点击事件。但其完全不可靠,如果猜错了,您将失去一个点击连接。


6
是的,确实如此。并且有跨浏览器的解决方案:https://dev59.com/AHE95IYBdhLWcg3wRL0t#32138108 - Dmitry Kochin
1
我检查了这些链接,我认为答案是正确的。你只能检测到 iframe 内部的点击事件,但无法知道具体点击了什么。 - user568021
我投反对票,只是因为它不完全正确。你所说的大部分是正确的,但是有一些变通方法,正如这个帖子上更受欢迎的答案所述。 - newms87

38
以下代码可以告诉您用户是否点击/悬停或移出 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>

您需要使用自己的链接替换iframe中的src。希望这能帮到您。 问候, Mo。


1
根据快速测试,在修复URL后,给定的示例在IE 8中似乎可以工作,在Chrome 14.0.835.186 m中有一定的可靠性,但在Firefox 6.0.2中完全无法工作。 - Matthew Flaschen
在Chrome中运行良好,但在Firefox v62中无法正常工作,因为当单击iframe时,_blur_事件不会被触发。 - slesh

16

刚找到了这个解决方案... 我尝试了一下,很喜欢它...

适用于桌面和移动端的跨域iframe!

不知道是否百分百可靠

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

愉快地编码


2
这个相同的答案(可能是稍微更好的版本)在一年前就已经发布在这个页面上了:https://dev59.com/AHE95IYBdhLWcg3wRL0t#23231136 - Ryan
对我有效。只需要在此之前加上 window.focus();,否则您必须在单击iframe之前在网站中点击一些内容。 - Grant

5
您可以通过在窗口元素上使用模糊事件来实现这一点。
以下是用于跟踪iframes上点击的jQuery插件(当单击iframe时,它将触发自定义回调函数):https://github.com/finalclap/iframeTracker-jquery 使用方法如下:
jQuery(document).ready(function($){
    $('.iframe_wrap iframe').iframeTracker({
        blurCallback: function(){
            // Do something when iframe is clicked (like firing an XHR request)
        }
    });
});

5

请查看http://jsfiddle.net/Lcy797h2/,这是我的冗长解决方案,但在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();

这是很棒的编码,完全符合我的需求。对Omar Jackman表示赞赏和感谢,他帮助我成功地捕捉到了YouTube广告的点击。 - saun4frsh

4
这对我来说在所有浏览器上都有效(包括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>

可以嵌入一个网页到另一个网页中。在这个例子中,一个宽度为400像素、高度为600像素、id为"idanmorblog"的iframe元素将会展示sidanmor.com网站的内容。


4

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

只需在iframe上覆盖一个不可见的层,在单击时返回并在触发mouseleave事件时向上移动!
需要使用jQuery。

此解决方案不会在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>


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接