You can use a hack called fragment id messaging. This should be cross-browser, provided the page containing your iframe is the top level. In other words, there are a total of two levels. Basically, the child sets the fragment of the parent, and the parent watches for this.
EDIT: Okay, there are fancy libraries to generalize this. But this should do what you want and no more:
parent.html
<html>
<head>
<script type="text/javascript">
function checkForClose()
{
if(window.location.hash == "#close_child")
{
var someIframe = document.getElementById("someId");
someIframe.parentNode.removeChild(someIframe);
}
else
{
setTimeout(checkForClose, 1000)
}
}
setTimeout(checkForClose, 1000);
</script>
</head>
<body>
<iframe name="someId" id="someId" src="child.html" height="800" width="600">foo</iframe>
</body>
</html>
child.html:
<html>
<head>
<script type="text/javascript">
setTimeout(function(){window.parent.location.hash = "close_child";}, 5000);
</script>
<body style="background-color: blue"></body>
</html>
EDIT2: Cross-domain and independently controlled are different. I dug into the (heavily minified/obfuscated) Polyvore code to see how it works (incidentally, it doesn't in Firefox). First remember that bookmarklets, such as the Clipper, live in the context of the page open when they start. In this case, the bookmarklet loads a script , which in turn runs an init function which generates an iframe, but also runs:
Event.addListener(Event.XFRAME, "done", cancel);
If you digg into addListener, you'll find (beautified):
if (_1ce2 == Event.XFRAME) {
if (!_1cb3) {
_1cb3 = new Monitor(function () {
return window.location.hash;
},
100);
Event.addListener(_1cb3, "change", onHashChange);
}
}
cancel includes:
removeNode(iframe);
Now, the only remaining piece is that the iframe page loads another script with a ClipperForm.init function that includes:
Event.addListener($("close"), "click", function () {
Event.postMessage(window.parent, _228d, "done");
});
So we see clearly they are using fragment ID messaging.