164

I understand that it is not possible to tell what the user is doing inside an iframe if it is cross domain. What I would like to do is track if the user clicked at all in the iframe. I imagine a scenario where there is an invisible div on top of the iframe and the the div will just then pass the click event to the iframe.

Is something like this possible? If it is, then how would I go about it? The iframes are ads, so I have no control over the tags that are used.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Russ Bradberry
  • 10,705
  • 17
  • 69
  • 85

23 Answers23

193

This is certainly possible. This works in Chrome, Firefox, and IE 11 (and probably others).

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>

Caveat: This only detects the first click. As I understand, that is all you want.

Caleb Taylor
  • 2,670
  • 2
  • 21
  • 31
Paul Draper
  • 78,542
  • 46
  • 206
  • 285
  • 4
    Also of note is that if you changes browser tabs it will fire the focus(); – Linnay Mar 31 '15 at 04:31
  • Nevermind, I figured that out. It should be: `if(document.activeElement = document.getElementById('iframe')) {` – Linnay Mar 31 '15 at 04:51
  • I know it's a little late to post something on here, but @Linnay or anybody else, how did you fix it so it doesn't fire the focus when you switch the tab? – Radu Jul 19 '15 at 20:28
  • 9
    It DOES NOT work in Firefox. JSFiddle contains mistake that conceal this: = instead of ===. There is crossbrowser solution (even in IE8): http://stackoverflow.com/a/32138108/1064513 – Dmitry Kochin Aug 21 '15 at 10:34
  • 12
    The blur event doesn't fire if user doesn't click into main document first! Also, this is not usable for detecting clicks to multiple iframes, as there is no event that fires when focus changes from one iframe to another (iframe's `blur` event doesn't fire). – Tomáš Kafka Feb 17 '16 at 15:43
  • to detect multiple click, you can call the `blur()` method of the `activeElement` but another caveat of this method is that if the user set the focus on the iframe from tab key, it will detect it as a click while it shouldn't. @DmitryKochin, you just have to wait a frame after the focus event occurred for this to work on FF too, the new `activeElement` is set after the event fired. – Kaiido Apr 23 '16 at 10:46
  • This seems to be the most sophisticated way of doing this as of yet. Good job. – Cory Kleiser Nov 02 '18 at 17:48
  • 1
    why there is dependency on focus(); – Prasad Shinde Dec 21 '18 at 12:08
  • Just tried, and this is the best one compared to others. It even works from mobile browser. – user198989 Mar 01 '19 at 08:09
  • Unfortunately I don't think is perfect solution as it works for right button click as well. – Kosmonaft May 30 '19 at 05:59
  • has anyone tried it in typescript? `window.removeEventListener()` doesn't work. – Deepak Kumar Jun 16 '19 at 08:45
  • @DeepakKumar the listener's function cannot be void. Add the word "return" in front of window.removeEventListener('blur', listener); – DSLuminary Jul 02 '19 at 20:49
  • but this does not propagate the click event into the clicked item within the iframe – mwa91 Sep 20 '19 at 06:44
  • This is the best solution I have found. I'm using it to close a popup menu in Wordpress using the old classic editor (tinyMCE using iframe). But to work on FF (v76.0.1), I needed to replace `window.addEventListener('blur' ...);` by `window.onblur = function;`. And then, to remove the listener `window.onblur = null`. – Léo Muniz Jun 11 '20 at 06:51
  • Is there any way if i want to detect on every click ? – Inderjeet Jul 11 '20 at 06:24
  • Why is `removeEventListener` needed? I do not get it. And if its removed why is the event still firing after that? – redanimalwar Jan 12 '21 at 16:11
  • @LéoMuniz Does not work in Firefox 84.0.1 at all. Not even with `window.onblur` – redanimalwar Jan 12 '21 at 16:20
  • Another Caveat: on iOS Safari, this won't work unless you tap a "clickable" element within the iframe, or the iframe has touch/click events attached to the document. – Caleb Taylor Sep 13 '21 at 21:05
  • this won't catch multiple clicks, only the first click on the iframe – Steven Delrue May 11 '22 at 07:29
122

This is small solution that works in all browsers even IE8:

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

You can test it here: http://jsfiddle.net/oqjgzsm0/

Dmitry Kochin
  • 3,830
  • 4
  • 22
  • 14
  • 1
    What if you have several iframes and you don't know their id? – shankshera Aug 28 '15 at 08:15
  • 1
    only cross-browser reliable solution that also works in latest FF! Thx a lot. **It deserves more upvotes** – BrainOverflow Oct 12 '15 at 11:33
  • Works fine for me! Saved me a headache! – Raphael Parent Dec 10 '15 at 19:52
  • 6
    @shankshera Just get elem.id, that's your iframe id :). See http://jsfiddle.net/oqjgzsm0/219/ – Tomáš Kafka Feb 17 '16 at 15:35
  • This is awesome! Much better than solutions I've seen that depend on the `blur` callback because you can tell that a user actually clicked the element. – Daniel Bonnell Mar 22 '16 at 17:58
  • 4
    I'm using this to track clicks on social like buttons. But because 3/4 of the ones I'm using use iframes, I need to track clicks in multiple iframes. I've updated the fiddle to allow for that: http://jsfiddle.net/oqjgzsm0/273/. It sets a new interval that checks to see if a click is outside the last clicked iframe. Then resets the original interval to check for clicks again. It doesn't track multiple clicks in the same iframe without a click outside of it. – brouxhaha Apr 07 '16 at 19:07
  • 18
    Apart from the fact that using a continuously looping interval at such rate is not a very good idea, this will detect false positives if user set focus on the iframe through tab key navigation – Kaiido Apr 23 '16 at 10:43
  • 1
    Thanks very much for your answer. It helped a lot. I can also propose a modification for it, in order to make it work not only for the first click, but for every click. In the if statement, instead of `clearInterval(monitor);` we can use `elem.blur();`. – Thanasis1101 Oct 30 '16 at 16:41
  • Adding the `elem.blur();` as @Thanasis suggests had an side effect for me: Keystrokes did not reach the underlying page inside the iframe. – Tsunamis Jun 27 '17 at 06:21
113

Based on Mohammed Radwan's answer I came up with the following jQuery solution. Basically what it does is keep track of what iFrame people are hovering. Then if the window blurs that most likely means the user clicked the iframe banner.

the iframe should be put in a div with an id, to make sure you know which iframe the user clicked on:

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

so:

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

... this keeps overiFrame at -1 when no iFrames are hovered, or the 'bannerid' set in the wrapping div when an iframe is hovered. All you have to do is check if 'overiFrame' is set when the window blurs, like so: ...

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

Very elegant solution with a minor downside: if a user presses ALT-F4 when hovering the mouse over an iFrame it will log it as a click. This only happened in FireFox though, IE, Chrome and Safari didn't register it.

Thanks again Mohammed, very useful solution!

patrick
  • 11,519
  • 8
  • 71
  • 80
  • I have +1-ed this answer, although, it has the following issues: 1. When there are multiple iframes, you click on one of them, then immediately on another - the second click is not detected. 2. Multiple clicks inside an iframe are not counted too. 3. Does not work correctly on mobile, because you can not do "hover" event with a finger. – Sych Apr 28 '15 at 09:53
  • The above script is used by me to detect clicks away from my site. Most advertising networks now serve banners in frames. If you click one and then quickly another one before you left on the first click, technically I want to know the last click that you actually left on. So in my case it's wanted behavior. It does fine detecting clicks on mobile banners too. So the hover must be launched right before the click is executed – patrick Apr 28 '15 at 22:20
  • Not working in case of svg elements in iframe content :( http://stackoverflow.com/questions/32589735/how-to-make-iframe-with-svg-focusable – Serhiy Sep 15 '15 at 18:18
  • @Serhiy, that's because you're not actually exiting the original page when clicking on the iframe... – patrick Sep 15 '15 at 21:59
  • 7
    This answer is the best of them, however, if you wish to receive every click into the iframe, **you need to take focus out of it once the user has clicked** in order to monitor further clicks. This should be added to the $(window).blur() section: `setTimeout(function(){ window.focus(); }, 0);`. Now, the user clicks, puts focus in the iframe, the script pulls that focus back, and can now monitor further focus changes from future clicks. – HelpingHand Sep 06 '16 at 15:49
  • Here is vanilla js version http://jsfiddle.net/lyubeto/3g11u1pt/ that includes setTimeout() for future clicks, as remarked by HelpingHand . – lyubeto Apr 11 '18 at 13:28
  • @lyubeto that example works only when clicking on the parent first then on the iframe, then on the parent again and then on the iframe and so on to progress the counter. (Firefox 57). – redanimalwar Apr 16 '20 at 21:06
  • @HelpingHand, the script was designed for pages that actually load another website on clicking the iframe (which is the case in advertising)... You're right though, if you want to count multiple clicks you need to reset, but why the timeout? Just adding 'window.focus()' to the $(window).blur() section would do the trick too, right? – patrick Apr 17 '20 at 10:15
  • 1
    @HelpingHand the user would lose focus in the iframe. Imagine that if the user was focusing an input in the iframe, the method would let them lose the focus. orz.... – xianshenglu Nov 12 '20 at 01:48
43

Is something like this possible?

No. All you can do is detect the mouse going into the iframe, and potentially (though not reliably) when it comes back out (ie. trying to work out the difference between the pointer passing over the ad on its way somewhere else versus lingering on the ad).

I imagine a scenario where there is an invisible div on top of the iframe and the the div will just then pass the click event to the iframe.

Nope, there is no way to fake a click event.

By catching the mousedown you'd prevent the original click from getting to the iframe. If you could determine when the mouse button was about to be pressed you could try to get the invisible div out of the way so that the click would go through... but there is also no event that fires just before a mousedown.

You could try to guess, for example by looking to see if the pointer has come to rest, guessing a click might be about to come. But it's totally unreliable, and if you fail you've just lost yourself a click-through.

bobince
  • 528,062
  • 107
  • 651
  • 834
  • 6
    Yes, it is. And there is crossbrowser solution: http://stackoverflow.com/a/32138108/1064513 – Dmitry Kochin Aug 21 '15 at 10:32
  • 1
    I checked these links and I think the answer is correct. You can only detect a click inside iframe but not what was clicked. – user568021 Jul 15 '16 at 09:02
  • I downvote, only because it is not totally true. Most of what you are saying is true, but there are workarounds as is the more popular answer on this thread. – newms87 Mar 08 '19 at 16:59
38

The following code will show you if the user click/hover or move out of the 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>

You need to replace the src in the iframe with your own link. Hope this'll help. Regards, Mo.

Konstantine Rybnikov
  • 2,457
  • 1
  • 22
  • 29
Mohammed Radwan
  • 381
  • 3
  • 2
  • 1
    Based on quick testing, the given example (after fixing the URL) seems to work in IE 8, somewhat reliably in Chrome 14.0.835.186 m, but not at all in Firefox 6.0.2. – Matthew Flaschen Sep 23 '11 at 23:54
  • Works fine for Chrome, but doesn't work for Firefox v62, because when click on iframe _blur_ event is not thrown – slesh Oct 02 '18 at 11:27
16

Just found this solution... I tried it, I loved it..

Works for cross domain iframes for desktop and mobile!

Don't know if it is foolproof yet

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

Happy coding

Grant
  • 5,709
  • 2
  • 38
  • 50
Tony
  • 185
  • 1
  • 3
  • 2
    This same answer (maybe a slightly better version) was posted a year earlier here on this same page: http://stackoverflow.com/a/23231136/470749 – Ryan Feb 19 '17 at 22:27
  • Worked for me. Just needed `window.focus();` beforehand, otherwise you have to click around in the site prior to clicking the iframe. – Grant Nov 24 '22 at 03:46
5

see http://jsfiddle.net/Lcy797h2/ for my long winded solution that doesn't work reliably in 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();
Boolean_Type
  • 1,146
  • 3
  • 13
  • 40
DiverseAndRemote.com
  • 19,314
  • 10
  • 61
  • 70
  • Its awesome coding man.... what actually i want...+1 to @Omar Jackman.. so much helpful to capture click on youtube advertisement – saun4frsh Oct 29 '13 at 13:43
5

You can achieve this by using the blur event on window element.

Here is a jQuery plugin for tracking click on iframes (it will fire a custom callback function when an iframe is clicked) : https://github.com/finalclap/iframeTracker-jquery

Use it like this :

jQuery(document).ready(function($){
    $('.iframe_wrap iframe').iframeTracker({
        blurCallback: function(){
            // Do something when iframe is clicked (like firing an XHR request)
        }
    });
});
Vince
  • 3,274
  • 2
  • 26
  • 28
4

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

Just make a invisible layer over the iframe that go back when click and go up when mouseleave event will be fired !!
Need jQuery

this solution don't propagate first click inside 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>
r1si
  • 1,136
  • 2
  • 19
  • 34
4

This works for me on all browsers (included 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>

sidanmor
  • 5,079
  • 3
  • 23
  • 31
4

Combining above answer with ability to click again and again without clicking outside 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 );
    });
Talha Asif
  • 61
  • 7
3

You can do this to bubble events to parent document:

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

Just extend the eventlist for more events.

Taner Topal
  • 921
  • 8
  • 14
  • I used 'touchend' event and it worked! Your answer helped me a lot! –  Jan 14 '16 at 15:47
3

I ran into a situation where I had to track clicks on a social media button pulled in through an iframe. A new window would be opened when the button was clicked. Here was my solution:

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();
pizzarob
  • 11,711
  • 6
  • 48
  • 69
3

Mohammed Radwan, Your solution is elegant. To detect iframe clicks in Firefox and IE, you can use a simple method with document.activeElement and a timer, however... I have searched all over the interwebs for a method to detect clicks on an iframe in Chrome and Safari. At the brink of giving up, I find your answer. Thank you, sir!

Some tips: I have found your solution to be more reliable when calling the init() function directly, rather than through attachOnloadEvent(). Of course to do that, you must call init() only after the iframe html. So it would look something like:

<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>
zone117x
  • 995
  • 2
  • 10
  • 18
1

This definitely works if the iframe is from the same domain as your parent site. I have not tested it for cross-domain sites.

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

Without jQuery you could try something like this, but again I have not tried this.

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

You can even filter your results:

$(window.frames['YouriFrameId']).mousedown(function(event){   
  var eventId = $(event.target).attr('id');      
  if (eventId == 'the-id-you-want') {
   //  do something
  }
});
Jonathan Tonge
  • 1,520
  • 19
  • 25
1

We can catch all the clicks. The idea is to reset focus on an element outside the iFrame after each click:

    <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

Voivarum
  • 21
  • 1
1

Assumptions -

  1. Your script runs outside the iframe BUT NOT in the outermost window.top window. (For outermost window, other blur solutions are good enough)
  2. A new page is opened replacing the current page / a new page in a new tab and control is switched to new tab.

This works for both sourceful and sourceless iframes

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

If a new tab is opened / same page unloads and the mouse pointer is within the Iframe, a click is considered

Sahil Maniar
  • 31
  • 1
  • 7
0

As found there : Detect Click into Iframe using JavaScript

=> We can use iframeTracker-jquery :

$('.carousel-inner .item').each(function(e) {
    var item = this;
    var iFrame = $(item).find('iframe');
    if (iFrame.length &gt; 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 =&gt; OK');
            }
        });
        console.log('IFrameTrackingRegistred =&gt; OK');
    }
})
juliomalves
  • 42,130
  • 20
  • 150
  • 146
Mickaël
  • 940
  • 11
  • 9
0

Based in the answer of Paul Draper, I created a solution that work continuously when you have Iframes that open other tab in the browser. When you return the page continue to be active to detect the click over the framework, this is a very common situation:

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

The Code is simple, the blur event detect the lost of focus when the iframe is clicked, and test if the active element is the iframe (if you have several iframe you can know who was selected) this situation is frequently when you have publicity frames.

The second event trigger a focus method when you return to the page. it is used the visibility change event.

freedeveloper
  • 3,670
  • 34
  • 39
0

Here is solution using suggested approaches with hover+blur and active element tricks, not any libraries, just pure js. Works fine for FF/Chrome. Mostly approache is same as @Mohammed Radwan proposed, except that I use different method proposed by @zone117x to track iframe click for FF, because window.focus is not working without addition user settings:

Makes a request to bring the window to the front. It may fail due to user settings and the window isn't guaranteed to be frontmost before this method returns.

Here is compound method:

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;
            });
    }
}
slesh
  • 1,902
  • 1
  • 18
  • 29
0

A colleague and I, we have a problem similar to that of Brian Trumpsett and found this thread very helpful. Our kiosk has animations inside iframes and we need to track the page activity to set a timer.

As suggested here, rather than tracking the clicks, we now detect the focus change at each click and change it back The following code is Okay on macOS with Safari and Chrome but does not work with FireFox (why?):

var eventListener = window.addEventListener('blur', function() {
if (document.activeElement.classList && document.activeElement.classList[0] == 'contentiFrame') {
    refresh(); //function you want to call on click
            setTimeout(function(){ window.focus(); }, 1);
}
    window.removeEventListener('blur', eventListener );
});

The problem is that, on Windows, it works neither with Chrome nor with FireFox and thus, our kiosk is not functional. Do you know why it is not working ? Do you have a solution to make it work on Windows ?

linus
  • 99
  • 2
  • 6
0

My approach was similar to that proposed by Paul Draper above. However, it didn't work in Firefox because activeElement did not update in time for the code to execute. So we wait a little bit.

This will also fire if you tab into the iframe. For my use case, it's fine, but you could filter for that keypress.

addEventListenerOnIframe() {
    window.addEventListener('blur', this.onBlur);
}


onBlur = () => {
    setTimeout(() => {
        let activeElement = document.activeElement;
        let iframeElement = document.querySelector('iframe');
        if (activeElement === iframeElement) {
            
            //execute your code here

            //we only want to listen for the first time we click into the iframe
            window.removeEventListener('blur', this.onBlur);
        }
    }, 500);
};
Chris Karpyszyn
  • 867
  • 10
  • 16
-2

I believe you can do something like:

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

using jQuery to accomplish this.

Daniel Sellers
  • 750
  • 4
  • 7