52

I'm writing a plugin for TinyMCE and have a problem with detecting click events inside an iframe.

From my search I've come up with this:

Loading iframe:

<iframe src='resource/file.php?mode=tinymce' id='filecontainer'></iframe>

HTML inside iframe:

<input type=button id=choose_pics value='Choose'>

jQuery:

//Detect click
$("#filecontainer").contents().find("#choose_pic").click(function(){
    //do something      
}); 

Other posts I've seen usually have a problem with different domains (this hasn't). But, still, the event isn't detected.

Can something like this be done?

Tonio Liebrand
  • 17,189
  • 4
  • 39
  • 59
Philip G
  • 4,098
  • 2
  • 22
  • 41

10 Answers10

66

I solved it by doing like this:

$('#filecontainer').load(function(){

        var iframe = $('#filecontainer').contents();

        iframe.find("#choose_pics").click(function(){
               alert("test");
        });
});
Philip G
  • 4,098
  • 2
  • 22
  • 41
  • 7
    `.load()` plays a key role here really – Ilia Ross Sep 25 '14 at 20:13
  • 11
    I get SecurityError: Failed to read the 'contentDocument' property from 'HTMLIFrameElement': Blocked a frame with origin... Looks like this is a security issues and gets blocked by Chrome. – genepool99 Feb 25 '16 at 19:52
  • 7
    jQuery 3: `$('#filecontainer').on('load', function(){` – Bob Stein Jun 02 '17 at 17:26
  • If anyone is interested in a "fully reproducible" version of the accepted answer see my post at the end,... - to save a few minutes for others ;) Can also be integrated in this answer with an edit, i just thought it might help others. – Tonio Liebrand Sep 07 '17 at 10:05
6

I'm not sure, but you may be able to just use

$("#filecontainer #choose_pic").click(function() {
    // do something here
});

Either that or you could just add a <script> tag into the iframe (if you have access to the code inside), and then use window.parent.DoSomething() in the frame, with the code

function DoSomething() {
    // do something here
}

in the parent. If none of those work, try window.postMessage. Here is some info on that.

tomb
  • 1,817
  • 4
  • 21
  • 40
5
$("#iframe-id").load( function() {
    $("#iframe-id").contents().on("click", ".child-node", function() {
        //do something
    });
});
Akash gupta
  • 316
  • 4
  • 4
4

I know this is old but the ID's don't match in your code one is choose_pic and one is choose_pics:

<input type=button id=choose_pics value='Choose'>

$("#filecontainer").contents().find("#choose_pic").click(function(){
    //do something      
}); 
KiwisTasteGood
  • 178
  • 2
  • 15
2

The tinymce API takes care of many events in the editors iframe. I strongly suggest to use them. Here is an example for the click handler

// Adds an observer to the onclick event using tinyMCE.init
tinyMCE.init({
   ...
   setup : function(ed) {
      ed.onClick.add(function(ed, e) {
           console.debug('Iframe clicked:' + e.target);
      });
   }
});
Thariama
  • 50,002
  • 13
  • 138
  • 166
1

Just posting in case it helps someone. For me, the following code worked perfect:

$(document).ready(function(){
     $("#payment_status_div").hide();
     var iframe = $('#FileFrame').contents();
     iframe.find("#take_payment").click(function(){
         $("#payment_status_div").show("slow");
     });
});

Where 'FileFrame' is the iframe id and 'take_payment' is the button inside iframe. Since my form inside the iframe is posted to a different domain, when used load, I got an error message saying:

Blocked a frame with origin "https://www.example.com" from accessing a frame with origin "https://secure-test.worldpay.com". Protocols, domains, and ports must match.

Abhijith Sasikumar
  • 13,262
  • 4
  • 31
  • 45
1

In my case there were two jQuery's, for the inner and outer HTML. I had four steps before I could attach inner events:

  1. wait for outer jQuery to be ready
  2. wait for iframe to load
  3. grab inner jQuery
  4. wait for inner jQuery to be ready

$(function() {   // 1. wait for the outer jQuery to be ready, aka $(document).ready
    $('iframe#filecontainer').on('load', function() {   // 2. wait for the iframe to load
        var $inner$ = $(this)[0].contentWindow.$;   // 3. get hold of the inner jQuery
        $inner$(function() {   // 4. wait for the inner jQuery to be ready
            $inner$.on('click', function () {   // Now I can intercept inner events.
                // do something
            });
        });
    });
});

The trick is to use the inner jQuery to attach events. Notice how I'm getting the inner jQuery:

        var $inner$ = $(this)[0].contentWindow.$;

I had to bust out of jQuery into the object model for it. The $('iframe').contents() approach in the other answers didn't work in my case because that stays with the outer jQuery. (And by the way returns contentDocument.)

Bob Stein
  • 16,271
  • 10
  • 88
  • 101
1

If anyone is interested in a "quick reproducible" version of the accepted answer, see below. Credits to a friend who is not on SO. This answer can also be integrated in the accepted answer with an edit,... (It has to run on a (local) server).

<html>
<head>
<title>SO</title>
<meta charset="utf-8"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>

<style type="text/css">
html,
body,
#filecontainer {
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<iframe src="http://localhost/tmp/fileWithLink.html" id="filecontainer"></iframe>

<script type="text/javascript">
$('#filecontainer').load(function(){

  var iframe = $('#filecontainer').contents();

  iframe.find("a").click(function(){
    var test = $(this);
    alert(test.html());
  });
});
</script>
</body>
</html>

fileWithLink.html

<html>
<body>
<a href="https://stackoverflow.com/">SOreadytohelp</a>
</body>
</html>
Tonio Liebrand
  • 17,189
  • 4
  • 39
  • 59
1

In my case, I was trying to fire a custom event from the parent document, and receive it in the child iframe, so I had to do the following:

var event = new CustomEvent('marker-metrics', {
    detail: // extra payload data here
});
var iframe = document.getElementsByTagName('iframe');
iframe[0].contentDocument.dispatchEvent(event)

and in the iframe document:

document.addEventListener('marker-metrics', (e) => {
  console.log('@@@@@', e.detail);
});
stevenlacerda
  • 1,187
  • 2
  • 9
  • 21
1

try

 $('#yourIframeid').on("load", function () {
            $(this).contents().on("contextmenu, keydown, mousedown, mouseup, click", function (e) {
               //do your thing
            });
           
        });

use $('#yourIframeid').on("load") if $('#yourIframeid').onload( does not work.