273

The website in the iframe isn't located in the same domain, but both are mine, and I would like to communicate between the iframe and the parent site. Is it possible?

ediblecode
  • 11,701
  • 19
  • 68
  • 116
Danny Fox
  • 38,659
  • 28
  • 68
  • 94

7 Answers7

481

With different domains, it is not possible to call methods or access the iframe's content document directly.

You have to use cross-document messaging.

parent -> iframe

For example in the top window:

myIframe.contentWindow.postMessage('hello', '*');

and in the iframe:

window.onmessage = function(e) {
    if (e.data == 'hello') {
        alert('It works!');
    }
};

iframe -> parent

For example in the top window:

window.onmessage = function(e) {
    if (e.data == 'hello') {
        alert('It works!');
    }
};

and in the iframe:

window.top.postMessage('hello', '*')
nnyby
  • 4,748
  • 10
  • 49
  • 105
user123444555621
  • 148,182
  • 27
  • 114
  • 126
  • 2
    thank you, but unfortunately it doesn't works in older browsers. – Danny Fox Feb 06 '12 at 13:25
  • @Pumbaa80 : Can you put a simple example? – Touko Aug 21 '12 at 07:54
  • 116
    In the parent: `window.onmesage = function()...`. In the iframe: `window.top.postMessage('hello', '*')` – user123444555621 Aug 21 '12 at 11:51
  • @Pumbaa80 when I do that, I get two errors in the chrome console, but still, it works. `Unsafe JavaScript attempt to access frame with URL file:///iframe.html from frame with URL file:///parent.html. Domains, protocols and ports must match.` – Zelenova Jun 16 '13 at 22:24
  • @Zelenova That may be due to a bug in Chrome: http://code.google.com/p/chromium/issues/detail?id=94618 – user123444555621 Jun 16 '13 at 22:56
  • 5
    It's not a bug. File urls can be very unsafe and browsers are treating them with ever increasing care. Back in the old days you could put a link to `file://C:/Windows/system32/whatever` on a webpage and make it point right into the user's system folder. These days browsers mostly ignore clicks on links like that. Run a webserver and access your code through that and you'll see the errors diappear. – Stijn de Witt Mar 27 '14 at 20:46
  • Can you provide a demo please. @Pumbaa80 – tvshajeer Mar 21 '15 at 05:32
  • 7
    As a good practice, never use the '*' for your target. In fact, MDN says - "Always provide a specific targetOrigin, not *, if you know where the other window's document should be located. Failing to provide a specific target discloses the data you send to any interested malicious site." – rodiwa Oct 29 '15 at 08:14
  • 3
    We can even use `window.frames[index]` to get child frame (` – phoenisx Oct 08 '17 at 05:34
  • I've done same thing for VSCode, see my blog post https://alfilatov.com/posts/how-to-pass-data-between-iframe-and-parent-window/ – Alex Filatov Sep 08 '20 at 22:57
  • As of this writing, March of 2021, cross-browser support for `window.onmessage` is not complete: https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onmessage#browser_compatibility – flyingace Mar 02 '21 at 22:23
  • @user123444555621 hey that's onmessage not onmesage :) I copy-and-paste and it does not work then i see this bug – ch271828n Jun 10 '21 at 05:54
102

In 2018 and modern browsers you can send a custom event from iframe to parent window.

iframe:

var data = { foo: 'bar' }
var event = new CustomEvent('myCustomEvent', { detail: data })
window.parent.document.dispatchEvent(event)

parent:

window.document.addEventListener('myCustomEvent', handleEvent, false)
function handleEvent(e) {
  console.log(e.detail) // outputs: {foo: 'bar'}
}

PS: Of course, you can send events in opposite direction same way.

document.querySelector('#iframe_id').contentDocument.dispatchEvent(event)
Wenfang Du
  • 8,804
  • 9
  • 59
  • 90
Stranger in the Q
  • 3,668
  • 2
  • 21
  • 26
  • 3
    Hello, do I have to be on the same domain to do it ? – Guillaume Harari Dec 13 '18 at 13:07
  • 3
    https://stackoverflow.com/questions/25098021/securityerror-blocked-a-frame-with-origin-from-accessing-a-cross-origin-frame – Stranger in the Q Dec 13 '18 at 13:33
  • 1
    It should be noted that `dispatchEvent` is supported in all major browsers. IE9 was the first version it came in, so most OSs now work with it. https://caniuse.com/#search=dispatchEvent – Dan Atkinson Aug 16 '19 at 12:48
  • 3
    I am not able to communicate from parent to iframe with this method. – Avan Sep 27 '19 at 08:44
  • Yeah I can't get it to work either, the iframe js is loading after the parent window so it is not there to receive the msg when sent. it only works from iframe to parent for me. – radtek Mar 18 '20 at 00:40
  • 1
    There is a simple solution to that Avan and @radtek. All you have to do is first dispatch an event from the iframe to the parent that notifies the parent that the iframe is loaded (essentially a "ready message"). The parent will be listening for messages and if it receives the "ready message" event, it can then reply to the iframe with whatever message you want to send. That way it sends the message to the iframe only once the iframe is loaded. And of course the iframe page will only send the message to the parent once it is loaded. – Cannicide Jun 01 '20 at 15:11
  • Already figured it out sometime ago, made a prototype repo - https://github.com/radzhome/django_sso_service – radtek Jun 02 '20 at 14:13
  • 1
    I tested it and it works only for same origin frames. Cross-origin will result in a `SecurityError` when accessing `window.parent` – Jérôme Beau Mar 22 '21 at 22:36
  • Make sure when you set the iframe to add the following for this to work: `` – Gal Bracha Apr 24 '22 at 18:17
  • Does dispatchEvent better then postMessage? From the answer https://stackoverflow.com/questions/30183600/difference-between-custom-event-and-postmessage postMessage is safer and support cross-domains – Michael Freidgeim Jun 01 '22 at 20:54
15

This library supports HTML5 postMessage and legacy browsers with resize+hash https://github.com/ternarylabs/porthole

Edit: Now in 2014, IE6/7 usage is quite low, IE8 and above all support postMessage so I now suggest to just use that.

https://developer.mozilla.org/en-US/docs/Web/API/Window.postMessage

jpillora
  • 5,194
  • 2
  • 44
  • 56
  • With the caveat that IE8/9 only support strings http://caniuse.com/#search=postmessage (See known issues) – Harry Nov 16 '16 at 14:28
  • this can be worked around by encoding your event objects to json and decode them on the other side. – codewandler May 11 '17 at 08:50
12

Use event.source.window.postMessage to send back to sender.

From Iframe

window.top.postMessage('I am Iframe', '*')
window.onmessage = (event) => {
    if (event.data === 'GOT_YOU_IFRAME') {
        console.log('Parent received successfully.')
    }
}

Then from parent say back.

window.onmessage = (event) => {
    event.source.window.postMessage('GOT_YOU_IFRAME', '*')
}

Updated:

postMessage should not work on cross domain, so the solution like this:

For example your website is: customer.com and your domain is my.com

You need to do like this

  • Create a js file (upload to CDN or your server) - my.com
  • Embed js file above to customer.com

Now from my.com, you can postMessage and above embed script can be received data from you.

Binh Ho
  • 3,690
  • 1
  • 31
  • 31
  • 4
    As of this writing, March of 2021, cross-browser support for `window.onmessage` is not complete: https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onmessage#browser_compatibility – flyingace Mar 02 '21 at 22:24
  • 1
    @flyingace The only caveat I'm aware of is cross-domain iframe messaging on IE. Are you aware of any gaps between modern browsers? – kevlened Oct 20 '21 at 18:39
  • For cross-browser or crossing domain, you can embed a script with the same domain to website and postMessage it as normal. Cross domain will not work. So embed script is a solution, not a workaround. – Binh Ho Feb 13 '23 at 14:06
  • 1
    THANKYOU!! lost so much hair over this. The only solution that actually worked – Sharkfin Mar 11 '23 at 14:28
2

After spending 2 days trying to get an iFrame posting messages back to the parent, a Vue application in my situation, I came across this excellent reference:

https://dev-bay.com/iframe-and-parent-window-postmessage-communication/

From the iframe to parent:

    const parentWindow = window.parent;
    class Message {
        constructor(type, body) {
          this.type = type;
          this.body = body;
        }
    };

    function sendMessage (windowObj, payload) {
        if(windowObj) {
          windowObj.postMessage(payload, "*");
        }
    };

    //Then call appropriately: 
    sendMessage(parentWindow, new Message("button-click", "Show Stats Overlay"));

In the parent, my Vue application mounted life cycle event, but reference the link for your own requirement:

    window.addEventListener("message", (e) => {
        var data = e.data;
        console.log("RECEIVED message from CHILD TO PARENT", data);
        
        var type = data.type;
        var body = data.body;

        if(type === "button-click" && body) {
          console.log("button-click RECEIVED FROM CHILD")
          //Additional functionality ...
        } else if (type === "text-msg" && body) {
          console.log("TEXT MESSAGE RECEIVED FROM CHILD");
          //Additional functionality ...
        }
      });

Please see reference for examples of communication from Parent to iFrame.

Hope this helps someone else.

CStock
  • 21
  • 1
2

the window.top property should be able to give what you need.

E.g.

alert(top.location.href)

See http://cross-browser.com/talk/inter-frame_comm.html

mattsven
  • 22,305
  • 11
  • 68
  • 104
sambomartin
  • 6,663
  • 7
  • 40
  • 64
0

You can also use

postMessage(message, '*');

gonzarodriguezt
  • 191
  • 3
  • 12
  • 3
    not too informative, I've tried to descrive the process in my blog post https://alfilatov.com/posts/how-to-pass-data-between-iframe-and-parent-window/ – Alex Filatov Sep 08 '20 at 22:57