I have a iframe (in my domain), that iframe has a file iframe.js.
My parent document has a file parent.js.
I need to call a function that is in parent.js, from a function that is in iframe.js.
I tried doing window.parent.myfunction() this function is in the parent.js file.
But, it didn't work. Only when I put the function on the parent page (I mean in the HTML), then it worked.
Any idea how to get this to work?
Try just parent.myfunction(). Also be 100% sure that the parent.js is included in your parent document.
I know this is an old question, but in case the accepted answer doesn't work (it didn't work for me) you could do this inside parent.js
window.myfunction = function () {
alert("I was called from a child iframe");
}
Now from the iframe you can call myfunction() like you initially wanted
window.parent.myfunction();
Window.postMessage() method safely enables cross-origin communication.
If you have access to parent page then any data can be passed as well as any parent method can be called directly from Iframe.
Parent page:
if (window.addEventListener) {
window.addEventListener("message", onMessage, false);
} else if (window.attachEvent) {
window.attachEvent("onmessage", onMessage, false);
}
function onMessage(event) {
// Check sender origin to be trusted
if (event.origin !== "http://example.com") return;
var data = event.data;
if (typeof(window[data.func]) == "function") {
window[data.func].call(null, data.message);
}
}
// Function to be called from iframe
function parentFuncName(message) {
alert(message);
}
Iframe code:
window.parent.postMessage({
'func': 'parentFuncName',
'message': 'Message text from iframe.'
}, "*");
References:
Cross-document messaging (https://html.spec.whatwg.org/multipage/comms.html#web-messaging)
Window.postMessage method (https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage)
Can I Use (http://caniuse.com/#search=postMessage)
Related
This question already has answers here:
Invoking JavaScript code in an iframe from the parent page
(17 answers)
Closed 5 years ago.
I have two pages a.aspx and b.aspx. a.aspx have an Iframe and I load the b.aspx page in that Iframe. Is it possible to call a javascript from b.aspx to a.aspx?
Thanks in advance
Let's say you have the following javascript function in b.aspx.
function DoSomething()
{
//Do you required tasks
}
And you want to call this function in a.aspx. a.aspx has b.aspx loaded in and iframe as follows.
<iframe id="fraExample" name="fraExample" scrolling="no" src="b.aspx"></iframe>
This is how you'll call function DoSomething written in b.aspx from a function written in a.aspx
function CallFunction()
{
// This function will be in a.aspx
// some tasks
var bFrame = document.getElementById('fraExample'); // Get the iframe
bFrame.contentWindow.DoSomething();
// some other tasks
}
This should work for you nice and easy!
One way to do this, which will also cover scenarios where you've got different origins for the outer page and the page in the IFrame (e.g. http://site1/a.aspx and http://site2/b.aspx) is to use the postMessage function.
This has the added advantage that you're "locking yourself in" to an API contract between a.aspx and b.aspx as, if you had a function called DoSomething in b.apx which was being called from a.aspx, if you decided to rename the function you'd need to make changes to both pages (and any other pages that hosted a.aspx in an IFrame). If you use the postMessage approach, the only place you'd need to change would be the listener for the "message" event in b.aspx.
If you put the following code in a.aspx:
function sendMessageToEveryIFrame(message)
{
/// <summary>Call a function against all frames</summary>
var frames = document.getElementsByTagName('iframe');
for (var i = 0; i < frames.length; i++)
{
try
{
var frame = frames[i];
frame.contentWindow.postMessage(message, "*");
}
catch (e)
{
// Code to handle errors would go here!
}
}
}
You could then call it (perhaps on a button click for testing purposes) by having:
sendMessageToEveryIFrame('Test message!');
You then need some code in b.aspx that reacts to messages coming in, for example:
// This wires up your function that processes the message
window.addEventListener("message", receiveMessage, false);
function receiveMessage(event)
{
// logic that reacts to the event/message goes here
alert(event.data);
}
When you press the button in a.aspx in the browser (or whatever else you're using to trigger the call) b.aspx should pop up an alert window containing the text "Test message!".
Your receiveMessage function can then dispatch the request and effectively act as a broker. So, if you had two functions inside b.aspx that you wanted to call, you could re-jig the code like this:
In a.aspx:
function callFrameFunction(functionName, parameters)
{
/// <summary>Call a function against all frames</summary>
var frames = document.getElementsByTagName('iframe');
for (var i = 0; i < frames.length; i++)
{
try
{
var frame = frames[i];
var message =
{
Function: function,
Parameters : parameters
};
frame.contentWindow.postMessage(message, "*");
}
catch (e)
{
// Code to handle errors would go here!
}
}
}
This means that callFrameFunction takes two parameters, the name of a function and the parameters for the function. You'd then update the code in b.aspx so that it looks more like this:
// This wires up your function that processes the message
window.addEventListener("message", receiveMessage, false);
function receiveMessage(event)
{
// logic that reacts to the event/message goes here
switch(event.data.FunctionName)
{
case "function 1":
alert('Function called was \'function 1\'');
break;
case "function 2":
alert('Function called was \'function 2\'');
break;
}
}
You can replace the calls to alert with calls to your functions, passing in the values in event.data.Parameters as appropriate.
I have an iframe with a form in it, and in that iframe I have the following:
// Send a message to the parent window
window.parent.postMessage({
event: 'submit'
}, '*');
The above is supposed to send a message to the parent window when the form is submitted.
In the parent window I have the following:
function receiveMessage(event) {
var origin = event.origin;
if (origin !== 'https://iframe.domain') {
return;
} else {
console.log('Submitted!');
}
}
window.addEventListener('message', receiveMessage, false);
The problem I seem to be having is that the code on the parent window is executing immediately without a message being sent from the iframe form being submitted. It is also executing over and over again. It logs "Submitted!" in the console over and over again for as long as I let it run.
How can this function run without the form being submitted to send the function, and why is it running over and over again?
In my iframe I moved the postMessage() to the footer, and checked for a div that is only available after my form is submitted. If the div exists I send a message to the parent window. This is the exact code in my iframe now.
// if the form has submitted and the confirmation message
// div exists send a messge to the parent window
if (jQuery('#gform_confirmation_wrapper_1').length) {
// Send a message to the parent window
parent.postMessage({
event: 'formSubmit'
}, '*');
}
On my parent window I created the function, checked the domain the message was coming from, and checked for the exact message being sent with if (event.data.event === 'formSubmit'). If that message, which was only sent from my iframe if the form's confirmation div existed, matched exactly formSubmitted then I pushed the event to the datalayer of Google Tag Manager. This is the exact code that is working on my dev site now.
// create function to push event to GTM if the iframe form has been submitted
function awReceiveMessage(event) {
// set variable to url that the message is coming from
var origin = event.origin;
// check if the message is coming from Polk
if (origin !== 'https://iframe.domain') {
//stop function if it is not coming from Polk
return;
} else {
// instantiating the GTM datalayer variable
var dataLayer = window.dataLayer || (window.dataLayer = []);
// if the message is formSubmit push the formSubmit event to the GTM datalayer
if (event.data.event === 'formSubmit') {
dataLayer.push({
'event': 'formSubmit'
});
}
}
}
// run awReceiveMessage() if a message is sent from the iframe to the parent
window.addEventListener('message', awReceiveMessage, false);
The above code is working and firing a GTM tag correctly on the parent page when the form is submitted in the iframe.
You need to check you have received the correct message.
function receiveMessage(event) {
if (event.origin !== 'https://iframe.domain') {
return;
} else if (event.data.event && event.data.event === 'submit') {
console.log('Submitted!');
}
}
window.addEventListener('message', receiveMessage, false);
I find it odd you are getting so many messages and I would suggest adding some code to dump them to the console to see what they are.
window.addEventListener('message', (event) => console.log(event), false);
I'm having an issue communicating from my parent window to the child iFrame. But in the other side, everything works perfectly.
Here is how I get the chil iFrame object in order to fire the postMessage function:
var iFrame = document.getElementById('Frame').contentWindow;
When I print it int he console, I get the following:
Window {parent: Window, opener: null, top: Window, length: 0, frames: Window…}
When I proceed to the postMessage function as follows:
iFrame.postMessage("message", "http://contoso.com");
It shows me an error when loading the page: iFrame.postMessage is not a function.
When I execute the postMessage in console, I get an undefined
What am I doing wrong ?
try this
var iFrame = document.getElementById('Frame');
iFrame.contentWindow.postMessage("message", "http://contoso.com");
I had this problem too. I found solution from this website https://www.viget.com/articles/using-javascript-postmessage-to-talk-to-iframes
I wasn't able to get this working using a querySelector approach.
What worked for me was the following. I'll refer to the two webpages as the parent that has an iframe on it and the src as the page inside the iframe.
On the src page, I post a message, with the parent url as the origin:
// src
window.parent.postMessage({
type: "connect",
url: window.location.href
}, "http://iframeparent.dev", );
Then in the parent page, I listen for this. It will have a property called source which is the iframe in question. This property can be posted to.
// parent
window.addEventListener("message", (event) => {
// this is the magic connection:
event.source;
}, false);
So you could do something like:
// parent
let sources = [];
window.addEventListener("message", (event) => {
sources.push(event.source);
}, false);
function something() {
sources.forEach(source => {
source.postMessage({some: "data"}, "http://iframesrc.dev")
})
}
Below code also works.
$('#id')[0].contentWindow.postMessage("hello world",targetOrigin);
There is a difference between jQuery selector and document.getElementById.
Document.getElementByID returns HTML DOM object.
jQuery selector returns jQuery object.
For more information please find below link.
document.getElementById vs jQuery $()
...but I do control part of the javascript inside the second domain (which integrates the iframe).
So, what I need is some workaround my problem. We have example2.com (this one holds the iframe) and example.com (this one is the original, within the iframe). Inside the iframe the user clicks a button that executes parent.redirectUser() and although I have that function defined in example2.com it fails to execute because it points the function as forbidden to access from within the iframe. Considering I can control the javascript in example2.com, is there any other way to workaround this situation? Thank you very much for your help...
Yes, you can do it.
You can use messages technique,
send message from child(example2.com):
parent.postMessage("Ok", "example.com");
in parent(example.com) you must add the following code:
//add eventlistener for message event
if (window.addEventListener) {
window.addEventListener("message", listener);
} else {
// IE8
window.attachEvent("onmessage", listener);
}
function listener(event) {
//check the message trust or not?
if (event.data == "Ok") {
//from parent you can call function, but function must be placed in global scope
redirectUser();
}
}
I have been stuck on this for hours.
I have a.html on http://example.com that contains an iframe with src to
b.html on http://subdomain.example.com. a.html has some JS code
to postMessage to the iframe.
The code to postMessage is simple:
iframe_window.postMessage('message', iframe_element.src)
But this way, Chrome throws an error:
Unable to post message to http://subdomain.example.com. Recipient has origin null.
I have also tried:
iframe_window.postMessage('message', 'http://subdomain.example.com')
But NO LUCK!
This is the ONLY WAY it works:
iframe_window.postMessage('message', '*')
But I have heard '*' is not good to use.
No problems in Firefox.
It looks like this might be an issue with the child iframe not being loaded at the time the signal is sent, thus iframe.src doesn't have the proper value.
I did some testing and got the same error as you, but when I wrapped the postMessage call in a setTimeout and waited 100ms then there was no error, which tells me that this is an initialisation race condition.
Here's how I implemented a cleaner solution without the setTimeout:
Parent:
window.addEventListener("DOMContentLoaded", function() {
var iframe = document.querySelector("iframe")
, _window = iframe.contentWindow
window.addEventListener("message", function(e) {
// wait for child to signal that it's loaded.
if ( e.data === "loaded" && e.origin === iframe.src.split("/").splice(0, 3).join("/")) {
// send the child a message.
_window.postMessage("Test", iframe.src)
}
})
}, false)
Child:
window.addEventListener("DOMContentLoaded", function() {
// signal the parent that we're loaded.
window.parent.postMessage("loaded", "*")
// listen for messages from the parent.
window.addEventListener("message", function(e) {
var message = document.createElement("h1")
message.innerHTML = e.data
document.body.appendChild(message)
}, false)
}, false)
This is a simple solution in which the child will signal to anyone that it's loaded (using "*", which is okay, because nothing sensitive is being sent.) The parent listens for a loaded event and checks that it's the child that it's interested in that's emitting it.
The parent then sends a message to the child, which is ready to receive it. When the child gets the message it puts the data in an <h1> and appends that to the <body>.
I tested this in Chrome with actual subdomains and this solution worked for me.
A shorter solution is to wrap the postMessage inside iframe_element.onload function.