Right, this is weird. I have done loads of Googling and found hundreds of articles which seem to point me in the right direction, but none seem to work.
Here's the latest incarnation of what I am trying to do:
Parent Page
<html>
<head>
<script>
$('#mainWindow').ready(function () {
$('#mainWindow').contents().find('#clickThis').live('click', function () {
alert('Click detected!');
});
});
</script>
</head>
<body>
<iframe id="mainWindow" name="mainWindow" src="myMainPage.aspx" style="border: 0; position:fixed; top:0; left:0; right:0; bottom:0; width:100%; height:100%"></iframe>
<iframe src="myOtherPage.aspx"></iframe>
</body>
</html>
Framed Page (sitting in mainWindow)
<html>
' LOADS OF STUFF INCLUDING:
<li id="clickThis">Click This</li>
</html>
So obviously what I am trying to do here is to run some code in the Parent Page when a user clicks the button in the child frame.
It needs to work live so if the page in the frame changes it is still captured by the parent (this element exists on all pages which will be loaded into the frame)
I have been able to run codes across iFrames, from parent to iFrame and from iFrame to iFrame following various other searches, but running from iFrame to parent is causing issues.
The above code does nothing, and neither do any of the other options I have tried!!
*Edit should add, all files are on the same server, the cross domain issue is not a problem
This works fine for me:
$('#myframe').contents().on('click', function () {
$('#result').text('clicked');
});
If you want to capture the events just on a part of the content document, use .contents().find(<selector>) (<selector> being e.g. 'body') instead of just .contents()
Something to watch out for: All of your pages must exist under the same document.domain or the JavaScript calls will fail. You will see this surfaced as a security exception in your console. This happens due to same-origin policy.
Way, way way too late of an answer, but I had the same issue and the code below works in all browsers:
$('#myiframe').on('load', function () {
$(this).contents().on('click', function () {
alert('Click detected!');
})
})
It attaches onclick handler but only after iframe has been loaded. I am leaving this here for posterity and for anybody else looking for an answer to the same question.
In case you want to capture some data as well inside the iframe, this will work for you:
There is an iframe element with multiple images in it. The iframe is rendered inside of parent page. Whichever image you click inside the iframe, that image's id is sent back to the parent.
Inside iframe:
<script>
function vote(e) {
myMessage = e;
window.parent.postMessage(myMessage, "*");
return false;
}
</script>
<body><div>
<img id="Image1" src="file:///Users/abcd/Desktop/1.jpeg" width="200px" onclick="vote(this.id)">
</div></body>
Inside parent page:
</script>
function displayMessage (evt) {
var message = "You voted " + evt.data;
document.getElementById("received-message").innerHTML = message;
}
if (window.addEventListener) {
// For standards-compliant web browsers
window.addEventListener("message", displayMessage, false);
}
else {
window.attachEvent("onmessage", displayMessage);
}
</script>
<body><div id="received-message"></div></body>
This will not only capture the click but also capture the id of the element being clicked inside the iframe. Also, this does not require JQuery.
why are you using find to bind an event listener to an element that might not be there? Why not just try:
$('#mainWindow').ready(function()
{
$('#clickThis').on('click',function()
{
alert($(this).html());//or something
});
});
BTW: since your code is inside the ready callback of #mainWindow, there is no need to use $('#mainWindow') a second time, as this will scan the DOM tree a second time. Just use this or $(this), as it will reference the mainWindow element anyway.
If on doesn't work for you, don't forget to check if you have the latest version of jQuery included, too. That, too can be the reason why on isn't working.
Related
I am looking for a solution to show a progress indicator when an iframe page changes, but prior to the iframe loading completely.
There are 100's of pages on Stack Overflow going over the differences between jQuery ready(), JavaScript window.load, document.load, DOMContentReady, on('pageinit'...) and after reading the differences on all these various techniques I'm now a bit stuck on how to accomplish trapping the event within the iframe.
So far I have only succeeded in capturing when the iframe has changed once the DOM is built. I would like to be able to detect when a page is about to load so I could have some sort of indicator/spinner in my header.
This is what I have so far (capturing the iframe change on the onload):
.....
<iframe id="rssID" src="http://feeds.reuters.com/reuters/oddlyEnoughNews"
onload="blerg()" style="width: 800px; height:600px"/>
......
$(document).on('pageinit','#index', function(){
alert('pageinit'); //gets called on first load
});
$(document).on('pageinit','#rssID', function(){
alert('pageinit rssFeed'); //nothing happens.
});
function blerg() {
var myIframe = document.getElementById("rssID");
myIframe.onload = func;
};
function func() {
alert("changed");
}
http://jsfiddle.net/Gdxvs/
It would appear that pageinit is the correct path but I have no idea on how to trap that within an iframe. I would prefer not using jQuery, but I'm not sure that is possible without huge amounts of code.
One final: Do I need to use a die("pageinit");
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?
I solved it by doing like this:
$('#filecontainer').load(function(){
var iframe = $('#filecontainer').contents();
iframe.find("#choose_pics").click(function(){
alert("test");
});
});
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.
$("#iframe-id").load( function() {
$("#iframe-id").contents().on("click", ".child-node", function() {
//do something
});
});
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
});
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);
});
}
});
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.
In my case there were two jQuery's, for the inner and outer HTML. I had four steps before I could attach inner events:
wait for outer jQuery to be ready
wait for iframe to load
grab inner jQuery
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.)
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>
SOreadytohelp
</body>
</html>
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);
});
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.
I am currently creating a page where upon clicking a link an iframe is inserted into a div and it's contents loaded. I do this using the following jQuery call:
$('#mydiv').html('<iframe src="sourcelink.html" frameborder="0" width="760" height="2400" scrolling="no"></iframe>');
Sometimes the source content loads very slowly and, as a result, it looks like nothing is happening. I would like to have a simple loading animation while the content is loading while the iframe's content loads. When the iframe finishes loading it's content should pop in and the loading animation should go away.
I've been considering a couple ways I could do this (e.g. having a separate loader div to simply swap the two in and out) but I'm not sure of what the 'best' approach to solving this problem is. Perhaps I shouldn't be using .html()? I'm open to suggestion if there is a more correct solution.
Is there any reason you can't listen to the onload event of the iframe itself? It should fire after the child content has loaded.
Something like this:
showLoader();
$('#mydiv').html('<iframe src="sourcelink.html" frameborder="0" width="760" height="2400" scrolling="no"></iframe>');
$('#mydiv iframe').load(function() { hideLoader(); }
So In my case doing the following did for me..
The HTML
<iframe id="ifrmReportViewer" src="" frameborder="0" style="overflow:hidden;width:100%; height: 1000px;"></iframe>
and I was loading the iFrame on button of a click, so here is the JS
$(document).ready(function () {
$('body').on('click','#btnLoadiFrame',function () {
ShowLoader();
$('#ifrmReportViewer').attr('src', url);
$('#ifrmReportViewer').load(function () {
HideLoader();
});
});
});
You need to define a method that allows your iframe to highlight that it has finished loading, e.g.:
Main page:
var ChildFrameComplete = function()
{
$("#progress").hide();
};
var LoadChildFrame = function()
{
$("#progress").show();
$("#mydiv").html("<iframe src=\"sourcelink.html\" ... ></iframe>");
};
sourcelink.html:
$(function()
{
parent.ChildFrameComplete();
});
If the iframe is being sourced from the same domain as the parent page domain, it can call methods defined in the parent page through the window.parent property.
Just give the containing element (this case #myDiv) a background of a throbber and the iframe contents will overlap this when it's done loading.
That's the simplest.
I am trying to do something similar to the Clipper application here http://www.polyvore.com/cgi/clipper
I can make the iframe appear in another website (cross domain). But I cannot make the "close" button to work.
This is what I used but it doesn't work for cross domain (basically remove the iframe element)
window.parent.document.getElementById('someId').parentNode.removeChild(window.parent.document.getElementById('someId'));
Can you help? Thanks.
You should use a library that abstracts this (e.g. http://easyxdm.net/wp/ , not tested). Fragment ID messaging may not work in all browsers, and there are better approaches, such as postMessage.
However, your example (Clipper) is using a hack called fragment id messaging. This can be cross-browser, provided the page containing your iframe is the top level. In other words, there are a total of two levels. Basically, the child sets the fragment of the parent, and the parent watches for this.
This is a similar approach to Clipper's:
parent.html
<html>
<head>
<script type="text/javascript">
function checkForClose()
{
if(window.location.hash == "#close_child")
{
var someIframe = document.getElementById("someId");
someIframe.parentNode.removeChild(someIframe);
}
else
{
setTimeout(checkForClose, 1000)
}
}
setTimeout(checkForClose, 1000);
</script>
</head>
<body>
<iframe name="someId" id="someId" src="child.html" height="800" width="600">foo</iframe>
</body>
</html>
child.html:
<html>
<head>
<script type="text/javascript">
setTimeout(function(){window.parent.location.hash = "close_child";}, 5000);
</script>
<body style="background-color: blue"></body>
</html>
EDIT2: Cross-domain and independently controlled are different. I dug into the (heavily minified/obfuscated) Polyvore code to see how it works (incidentally, it doesn't in Firefox). First remember that bookmarklets, such as the Clipper, live in the context of the page open when they start. In this case, the bookmarklet loads a script , which in turn runs an init function which generates an iframe, but also runs:
Event.addListener(Event.XFRAME, "done", cancel);
If you digg into addListener, you'll find (beautified):
if (_1ce2 == Event.XFRAME) {
if (!_1cb3) {
_1cb3 = new Monitor(function () {
return window.location.hash;
},
100);
Event.addListener(_1cb3, "change", onHashChange);
}
}
cancel includes:
removeNode(iframe);
Now, the only remaining piece is that the iframe page loads another script with a ClipperForm.init function that includes:
Event.addListener($("close"), "click", function () {
Event.postMessage(window.parent, _228d, "done");
});
So we see clearly they are using fragment ID messaging.
Try hiding the contents of the iframe, and don't worry about actually getting rid of the iframe element in the parent.
There is another implementation of the old hash hack. It's backwards compatible, easy javascript-only, and very easy to implement:
http://www.onlineaspect.com/2010/01/15/backwards-compatible-postmessage/
Suppose I have a page located at www.example.com/foo, and it contains an <iframe> with src="http://www.example.com/bar". I want to be able to fire an event from /bar and have it be heard by /foo. Using the Prototype library, I've tried doing the following without success:
Element.fire(parent, 'ns:frob');
When I do this, in Firefox 3.5, I get the following error:
Node cannot be used in a document other than the one in which it was created" code: "4
Line 0
Not sure if that's related to my problem. Is there some security mechanism that's preventing scripts in /bar from kicking off events in /foo?
Events can be handled by a function defined the parent window if the iframe is a page from the same domain (see MDC's article on Same Origin Policy); however, events will not bubble up from the iframe to the parent page (at least not in my tests).
I haven't tested this cross-browser yet, but it works in FF.
In the iFrame you can fire on the element parent.document:
Event.observe(window, 'load', function() {
parent.document.fire('custom:event');
});
and in the parent frame you can catch it with:
document.observe('custom:event', function(event) { alert('gotcha'); });
rather then catch an event on the main page, you can catch the event at the iframe, and call a function on the main page.
<-- main page -->
function catchIt()
{
// code here
}
<-- iframe page -->
function doIt()
{
top.catchIt();
}
<a onclick="doIt();">test</a>
i think it would work but didnt test it
This should work as expected, to do what you need:
index.html:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script>
$(function(){
$(document).on('eventhandler', function() {
alert('event was fired');
});
});
</script>
iframe.html:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script>
$(function(){
parent.$(parent.document).trigger('eventhandler');
});
</script>