Invoking JavaScript code in an iframe from the parent page
Asked Answered
D

17

678

Basically, I have an iframe embedded in a page and the iframe has some JavaScript routines I need to invoke from the parent page.

Now the opposite is quite simple as you only need to call parent.functionName(), but unfortunately, I need exactly the opposite of that.

Please note that my problem is not changing the source URL of the iframe, but invoking a function defined in the iframe.

Doubloon answered 30/10, 2008 at 19:27 Comment(1)
Note when debugging you can do things like window.location.href or parent.location.href to view the url of the iframe, if you want to verify that you have a reference to the iframe you are looking for.Exhalant
C
610

Assume your iFrame's id is "targetFrame" and the function you want to call is targetFunction():

document.getElementById('targetFrame').contentWindow.targetFunction();

You can also access the frame using window.frames instead of document.getElementById.

// this option does not work in most of latest versions of chrome and Firefox
window.frames[0].frameElement.contentWindow.targetFunction(); 
Camillacamille answered 30/10, 2008 at 19:35 Comment(14)
works in FF 7, Chrome 12, IE 8/9, and Safari (not sure of this version)Tris
This solution does not work for my gadget, here is my code document.getElementById('remote_iframe_0').contentWindow.my.create_element_gadg‌​et('verify_user');" remote_iframe_0 is created programmaticaly by a apache shindig server but window.parent.document.getElementById('remote_iframe_0').contentWindow.my.create_element_gadg‌​et('verify_user');" worksEwan
this doesn't work when the function in the iframe is defined as a jQuery function.Frontwards
@Dirty Henry what do you mean by "a jQuery function?" jQuery is a JavaScript library.Camillacamille
@JoelAnair He probably means jQuery object. i.e. $('iframe'). You'd need to do $('iframe')[0] or whatever to get the specific DOM element you want.Penury
@GiovanniBitliner Untrue. I just tested it in Chrome v31 and it works fine.Juice
@Mark Amery, I get SecurityError: Blocked a frame with origin "http://localhost:3000" from accessing a cross-origin frame. when I try it in Chrome 36.0.1985.125.Febrifacient
@JellicleCat That's because the parent page and the iframe have different hosts, making it a cross-origin frame, as the message tells you. As long as the protocol, domain and port of the parent page and iframe match, everything will work fine.Juice
I added an example on how to use the window.frames method mentioned.Griner
How to do this with cross-domain iframes?Gregory
@JoelAnair I'm trying to get the scroll position of the iFrame page (same origin). How would I call the function onscroll? I have in the iframe tag onscroll=targetFunction(), but it's not calling the function.Siegfried
There is now a standardized and secure way to do this, including cross-domain - channel messaging. See developer.mozilla.org/en-US/docs/Web/API/Channel_Messaging_API/…Godhead
Just a note if you are working with JQuery and are trying to accomplish this, then you need to make sure that you are not mixing vanilla JS and JQuery calls. I found the answer to help me with this here https://mcmap.net/q/64826/-contentwindow-of-a-dynamically-created-iframe-is-undefinedEdveh
where do you add those lines? index.html? (if so where in there?)Bloomy
A
163

There are some quirks to be aware of here.

  1. HTMLIFrameElement.contentWindow is probably the easier way, but it's not quite a standard property and some browsers don't support it, mostly older ones. This is because the DOM Level 1 HTML standard has nothing to say about the window object.

  2. You can also try HTMLIFrameElement.contentDocument.defaultView, which a couple of older browsers allow but IE doesn't. Even so, the standard doesn't explicitly say that you get the window object back, for the same reason as (1), but you can pick up a few extra browser versions here if you care.

  3. window.frames['name'] returning the window is the oldest and hence most reliable interface. But you then have to use a name="..." attribute to be able to get a frame by name, which is slightly ugly/deprecated/transitional. (id="..." would be better but IE doesn't like that.)

  4. window.frames[number] is also very reliable, but knowing the right index is the trick. You can get away with this eg. if you know you only have the one iframe on the page.

  5. It is entirely possible the child iframe hasn't loaded yet, or something else went wrong to make it inaccessible. You may find it easier to reverse the flow of communications: that is, have the child iframe notify its window.parent script when it has finished loaded and is ready to be called back. By passing one of its own objects (eg. a callback function) to the parent script, that parent can then communicate directly with the script in the iframe without having to worry about what HTMLIFrameElement it is associated with.

Aldis answered 30/10, 2008 at 20:27 Comment(4)
Great stuff! Especially your suggestion 5. has shown to be extremely valuable. It solved me a lot of headaches when trying to access iFrame functions from the parent in Chrome. Passing the function object out from the iFrame made it run like a charm in Chrome, FF and even IE from 9 down to 7 and also made it very easy to check if the function had loaded already. Thanks!Element
Number 3 works, but better if you do it like @le dorfier put it in his comment. Notice the methode() part.Feet
Please also note that due to security restrictions in modern browsers (tested FF29 and Chrome34) it matters a lot where you place your function call. Just calling the function from a script tag or $(document).ready() will not work. Instead put the call inside the body's onload tag, or in an anchor <a href="javascript:doit();">do it</a> as suggested elswhere (see #921887). Passing the function call to a script as a callback also usually works.Oceanography
@chovy: No, see the clarification in Vivek's answer about this.Aldis
H
74

Calling a parent JS function from iframe is possible, but only when both the parent and the page loaded in the iframe are from same domain i.e. example.com, and both are using same protocol i.e. both are either on http:// or https://.

The call will fail in below mentioned cases:

  1. Parent page and the iframe page are from different domain.
  2. They are using different protocols, one is on http:// and other is on https://.

Any workaround to this restriction would be extremely insecure.

For instance, imagine I registered the domain superwinningcontest.example and sent out links to people's emails. When they loaded up the main page, I could hide a few iframes in there and read their Facebook feed, check recent Amazon or PayPal transactions, or--if they used a service that did not implement sufficient security--transfer money out of their accounts. That's why JavaScript is limited to same-domain and same-protocol.

Hydrometeor answered 18/4, 2010 at 17:11 Comment(3)
The workaround is to use the beautiful and dangerous en.wikipedia.org/wiki/Cross-document_messagingThermosiphon
Does anyone know if a different sub-domain will cause this to fail? I'm having difficulty debugging an issue that I believe is related to this - the iframe is calling a parent window function, but the call isn't happening.Dovekie
Note, it would also fail for different port numbers. i.e. abc.com:80 != abc.com:8080Expeditionary
H
42

In the IFRAME, make your function public to the window object:

window.myFunction = function(args) {
   doStuff();
}

For access from the parent page, use this:

var iframe = document.getElementById("iframeId");
iframe.contentWindow.myFunction(args);
Hellhole answered 30/10, 2008 at 19:39 Comment(10)
Great Answer. Not sure of the convention here. I have a follow up question. If I should start a new Question, I will. I have one problem. My page's iframe is populated dynamically. It contains a button whose onclick() event simply calls window.print(). This prints the content of the iframe perfectly. But when the iframe's page's public function calls window.print() and if the parent page calls the public function, the content of the parent is printed. How should I code this so the call to window.print() in the public function behaves as it does in the onclick event?Auto
@Auto You don't want to call onclick. You want to call iframe.contentWindow.print().Hellhole
Unfortunately, that does not work. Maybe I should start a new Question and document my code? Either way, the parent page content prints too. document.getElementById('myFrame').contentWindow.print();' and the pubic function printContent()` (not the oncick event handler) which calls window.print() was called this way: document.getElementById('myFrame').contentWindow.printContent();Auto
@Auto - Yes, then it's best to start a new question. Unless the parent window is on a different domain than the iframe. Then nothing you try will ever work.Hellhole
What I reported here seems to be an IE problem (testing in IE8). No problem in current version of Chrome. Have yet to test other browsers however. For those interested, see this jsFiddle (I think this is a good example of dynamic iframe loading and calling a public function from the parent.)Auto
and those interested, I've asked a follow up Question here: Invoking javascript in iframe from parent page, IE issueAuto
I was having issues with calling an inner function from a frame parent in FF23 beta, using window.functionName in the declaration (as opposed to var functionName) resolved the permission error I was encountering, despite explicit document.domain use in all involved frames. +1Dragline
bestest one ever i found to communicate with parent i don't realy want security i just want to communicate thanks dudeBeckford
This is not as much of a good solution, at least it didnt work for me, i was getting CORS errors. In that case, try using something like this (copy-pasted from an answer further below): document.getElementById('my_iframe').contentWindow.postMessage('foo','*');Slone
@Nobody Nothing in my answer says that this would work across domains. Not even the question says anything about crossing domain borders. If you get CORS errors, your issue is a completely different one.Hellhole
C
28

If the iFrame's target and the containing document are on a different domain, the methods previously posted might not work, but there is a solution:

For example, if document A contains an iframe element that contains document B, and script in document A calls postMessage() on the Window object of document B, then a message event will be fired on that object, marked as originating from the Window of document A. The script in document A might look like:

var o = document.getElementsByTagName('iframe')[0];
o.contentWindow.postMessage('Hello world', 'http://b.example.org/');

To register an event handler for incoming events, the script would use addEventListener() (or similar mechanisms). For example, the script in document B might look like:

window.addEventListener('message', receiver, false);
function receiver(e) {
  if (e.origin == 'http://example.com') {
    if (e.data == 'Hello world') {
      e.source.postMessage('Hello', e.origin);
    } else {
      alert(e.data);
    }
  }
}

This script first checks the domain is the expected domain, and then looks at the message, which it either displays to the user, or responds to by sending a message back to the document which sent the message in the first place.

via http://dev.w3.org/html5/postmsg/#web-messaging

Cajole answered 29/10, 2011 at 10:22 Comment(2)
easyXDM provides an abstraction over PostMessage for older browsers (including a Flash (LocalConnection) fallback for the stubborn dinosaurs).Kaluga
that's great... after trying above answers finally i got your answer, thanks!Apoloniaapolune
S
13

Some of these answers don't address the CORS issue, or don't make it obvious where you place the code snippets to make the communication possible.

Here is a concrete example. Say I want to click a button on the parent page, and have that do something inside the iframe. Here is how I would do it.

parent_frame.html

<button id='parent_page_button' onclick='call_button_inside_frame()'></button>

function call_button_inside_frame() {
   document.getElementById('my_iframe').contentWindow.postMessage('foo','*');
}

iframe_page.html

window.addEventListener("message", receiveMessage, false);

function receiveMessage(event)
    {
      if(event) {
        click_button_inside_frame();
    }
}

function click_button_inside_frame() {
   document.getElementById('frame_button').click();
}

To go the other direction (click button inside iframe to call method outside iframe) just switch where the code snippet live, and change this:

document.getElementById('my_iframe').contentWindow.postMessage('foo','*');

to this:

window.parent.postMessage('foo','*')
Sprague answered 7/10, 2017 at 16:42 Comment(1)
The parent.postMessage method seem to be still reliable between frames.Mould
H
11

Quirksmode had a post on this.

Since the page is now broken, and only accessible via archive.org, I reproduced it here:

IFrames

On this page I give a short overview of accessing iframes from the page they’re on. Not surprisingly, there are some browser considerations.

An iframe is an inline frame, a frame that, while containing a completely separate page with its own URL, is nonetheless placed inside another HTML page. This gives very nice possibilities in web design. The problem is to access the iframe, for instance to load a new page into it. This page explains how to do it.

Frame or object?

The fundamental question is whether the iframe is seen as a frame or as an object.

  • As explained on the Introduction to frames pages, if you use frames the browser creates a frame hierarchy for you (top.frames[1].frames[2] and such). Does the iframe fit into this frame hierarchy?
  • Or does the browser see an iframe as just another object, an object that happens to have a src property? In that case we have to use a standard DOM call (like document.getElementById('theiframe')) to access it. In general browsers allow both views on 'real' (hard-coded) iframes, but generated iframes cannot be accessed as frames.

NAME attribute

The most important rule is to give any iframe you create a name attribute, even if you also use an id.

<iframe src="iframe_page1.html"
    id="testiframe"
    name="testiframe"></iframe>

Most browsers need the name attribute to make the iframe part of the frame hierarchy. Some browsers (notably Mozilla) need the id to make the iframe accessible as an object. By assigning both attributes to the iframe you keep your options open. But name is far more important than id.

Access

Either you access the iframe as an object and change its src or you access the iframe as a frame and change its location.href.

document.getElementById('iframe_id').src = 'newpage.html'; frames['iframe_name'].location.href = 'newpage.html'; The frame syntax is slightly preferable because Opera 6 supports it but not the object syntax.

Accessing the iframe

So for a complete cross–browser experience you should give the iframe a name and use the

frames['testiframe'].location.href

syntax. As far as I know this always works.

Accessing the document

Accessing the document inside the iframe is quite simple, provided you use the name attribute. To count the number of links in the document in the iframe, do frames['testiframe'].document.links.length.

Generated iframes

When you generate an iframe through the W3C DOM the iframe is not immediately entered into the frames array, though, and the frames['testiframe'].location.href syntax will not work right away. The browser needs a little time before the iframe turns up in the array, time during which no script may run.

The document.getElementById('testiframe').src syntax works fine in all circumstances.

The target attribute of a link doesn't work either with generated iframes, except in Opera, even though I gave my generated iframe both a name and an id.

The lack of target support means that you must use JavaScript to change the content of a generated iframe, but since you need JavaScript anyway to generate it in the first place, I don't see this as much of a problem.

Text size in iframes

A curious Explorer 6 only bug:

When you change the text size through the View menu, text sizes in iframes are correctly changed. However, this browser does not change the line breaks in the original text, so that part of the text may become invisible, or line breaks may occur while the line could still hold another word.

Handcrafted answered 30/10, 2008 at 19:31 Comment(3)
That link appears to be broken.Onanism
And to make matters worse, I cant find the related page nowhere on quirksmode.Closed
FWIW - web.archive.org/web/20080225210716/http://www.quirksmode.org/js/…Ri
D
10

Just for the record, I've ran into the same issue today but this time the page was embedded in an object, not an iframe (since it was an XHTML 1.1 document). Here's how it works with objects:

document
  .getElementById('targetFrame')
  .contentDocument
  .defaultView
  .targetFunction();

(sorry for the ugly line breaks, didn't fit in a single line)

Doubloon answered 30/6, 2009 at 12:48 Comment(0)
P
8

The IFRAME should be in the frames[] collection. Use something like

frames['iframeid'].method();
Purgatorial answered 30/10, 2008 at 19:36 Comment(1)
This answer could certainly use some more information, as there's definitely some other considerations. For one, I assume the dev needs to make method a property of the iframe's window object.Favourite
U
7

I found quite an elegant solution.

As you said, it's fairly easy to execute code located on the parent document. And that's the base of my code, do to just the opposite.

When my iframe loads, I call a function located on the parent document, passing as an argument a reference to a local function, located in the iframe's document. The parent document now has a direct access to the iframe's function thru this reference.

Example:

On the parent:

function tunnel(fn) {
    fn();
}

On the iframe:

var myFunction = function() {
    alert("This work!");
}

parent.tunnel(myFunction);

When the iframe loads, it will call parent.tunnel(YourFunctionReference), which will execute the function received in parameter.

That simple, without having to deal with the all the non-standards methods from the various browsers.

Unerring answered 8/2, 2014 at 17:44 Comment(5)
Hi, can you confirm this "That simple, without having to deal with the all the non-standards methods from the various browsers."?Derbent
I use this method on various projects, seems to works very well. I haven't personally tested all the browsers in circulation, but I never got a bug report on it neither.Unerring
Works like a charm, IE 11, Chrome ~50.Afire
Guess that's not cross-domain.. Is it?Gregory
@TusharShukla Unfortunately no, it's not.Yun
S
4

Continuing with JoelAnair's answer:

For more robustness, use as follows:

var el = document.getElementById('targetFrame');

if(el.contentWindow)
{
   el.contentWindow.targetFunction();
}
else if(el.contentDocument)
{
   el.contentDocument.targetFunction();
}

Workd like charm :)

Swage answered 13/10, 2011 at 17:15 Comment(0)
L
4

If you want to invoke the JavaScript function on the Parent from the iframe generated by another function ex shadowbox or lightbox.

You should try to make use of window object and invoke parent function:

window.parent.targetFunction();
Lex answered 5/12, 2011 at 5:43 Comment(2)
I believe the OP is trying to go the other directionAbrams
for someone like me who came looking for the inverse communication, you are a life and time saverAerothermodynamics
S
3

Folowing Nitin Bansal's answer

and for even more robustness:

function getIframeWindow(iframe_object) {
  var doc;

  if (iframe_object.contentWindow) {
    return iframe_object.contentWindow;
  }

  if (iframe_object.window) {
    return iframe_object.window;
  } 

  if (!doc && iframe_object.contentDocument) {
    doc = iframe_object.contentDocument;
  } 

  if (!doc && iframe_object.document) {
    doc = iframe_object.document;
  }

  if (doc && doc.defaultView) {
   return doc.defaultView;
  }

  if (doc && doc.parentWindow) {
    return doc.parentWindow;
  }

  return undefined;
}

and

...
var el = document.getElementById('targetFrame');

var frame_win = getIframeWindow(el);

if (frame_win) {
  frame_win.targetFunction();
  ...
}
...
Soursop answered 3/8, 2012 at 12:40 Comment(0)
Y
2
       $("#myframe").load(function() {
            alert("loaded");
        });
Yseulta answered 21/11, 2008 at 20:8 Comment(0)
O
2

Same things but a bit easier way will be How to refresh parent page from page within iframe. Just call the parent page's function to invoke javascript function to reload the page:

window.location.reload();

Or do this directly from the page in iframe:

window.parent.location.reload();

Both works.

Organism answered 30/8, 2011 at 6:44 Comment(0)
E
1

Try just parent.myfunction()

Enthusiast answered 14/12, 2011 at 11:33 Comment(2)
#12960994Derbent
@Enthusiast The OP wants to call a function in an iframe from the parent window, not the reverse.Roundy
T
-3

Use following to call function of a frame in parent page

parent.document.getElementById('frameid').contentWindow.somefunction()
Torpedo answered 5/8, 2014 at 12:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.