Reputation: 15846
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.
Upvotes: 20
Views: 45079
Reputation: 321
A shorter solution is to wrap the postMessage inside iframe_element.onload function.
Upvotes: 1
Reputation: 1083
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.
Upvotes: 33