The scenario: A typical meeting, they want to pop up our search screen, select an item and have it post back via Javascript the selected item. “Sure, no problem” I say. “Can you do cross domain Javascript like that” their dev asks, and I think for a second about all the IFrame proxy hacks I’ve seen and say “yeah, no problems”.
Doh.
So I get back to work, and talk to Ben-out-local-Javascript-god, who points me at the location-fragment for communication hack (see, for example Cross Domain Frame Communication with Fragment Identifiers (for Comet?)) and away I go.
I quickly discover that all the documentation on that is for use with frames, not windows, and windows work slightly different. For example, the parent window can’t poll the child for it’s location.hash, nor can the child set the parent’s location.hash.
Doh Doh.
Anyway to cut a long story short and to document it for anyone else, you can make it work. The child window needs to contain a iframe from the parent domain, then proxy communication though that. Modern non IE browsers can use Window.postMessage to do the same thing (Actually, IE8 supports Window.postMessage, too, but I couldn’t get it to work properly).
The core of the IE specific code looks like this. The child window uses this object:
/*
XDCommsChild - Cross Domain Communication, Child Object.
Create this in the child window with the address of the xdomaincomms.html file on the same domain as the parent window
*/
var XDCommsChild = function(xdomaincommsAddress) {
this.xdomaincommsAddress = xdomaincommsAddress;
this.isIe = isInternetExplorer();
if (isIe) {
// need hidden frame for communication. IE8 is supposed to support postMessage, but I can't get it to work properly
document.writeln("")
}
this.postBack = function(data) {
if (isIe) {
// this method tested against IE6,7 & 8
window.open(this.xdomaincommsAddress + '#data=' + data, 'xdcomms'); // MUST use window.open. frame.src or frame.location both fail
} else {
// for everything else - tested against Firefox 3, Chrome, Safari
window.opener.postMessage(data, '*'); // should really restrict the domain data can come from here
}
}
this.postBackAndCloseWindow = function(data){
this.postBack(data);
setTimeout("window.close()", 200); // need to use a timeout to make sure the javascript in the frame has executed if we are using that communication model
}
}
Then the included proxy frame (deployed on the parent domain) looks like this:
var hash = null;
function sendData() {
hash = location.hash;
if (hash != null && hash.length > 1) {
window.parent.opener.location.hash=hash;
location.hash = '#';
}
}
setInterval("sendData()", 50);
The the parent window uses this object:
/*
XDCommsParent - Cross Domain Communication, Parent Object.
Create this in the parent window with a callback function that will recieve data.
*/
var XDCommsParent = function(dataRecievedCallback) {
this.dataRecievedCallback = dataRecievedCallback;
this.isIe = isInternetExplorer();
this.receiveMessage = function(event) {
dataRecievedCallback(event.data);
}
if (this.isIe) {
window.setInterval("pollForData()", 200);
} else {
window.addEventListener("message", this.receiveMessage, false);
}
}
The final piece of the solution is calling it from the child window. I do this:
<button onclick="javascript:xDCommsChild.postBackAndCloseWindow(document.getElementById('thevalue').value);">Send to Parent Window</button>