views:

921

answers:

2

I am trying to do something similar to the Clipper application here http://www.polyvore.com/cgi/clipper

I can make the iframe appear in another website (cross domain). But I cannot make the "close" button to work.

This is what I used but it doesn't work for cross domain (basically remove the iframe element)

window.parent.document.getElementById('someId').parentNode.removeChild(window.parent.document.getElementById('someId'));    

Can you help? Thanks.

+4  A: 

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.

Matthew Flaschen
This is too complicated above what I need. I only need to do the closing of iframe. Any simple way to do it? If not, can you show example how this one does it? Thanks
HP
I don't have control over the parent document though. That's why I stated "cross domain". Just like the Polyvore example, they don't control the parent too but they still can close the iframe. Strange!
HP
As I just explained above, they /do/ control the parent, and they /are/ using fragment ID messaging.
Matthew Flaschen
A: 

Try hiding the contents of the iframe, and don't worry about actually getting rid of the iframe element in the parent.

Bruce
How to hide? I got this error Permission denied for <http://xxx.com> to get property Window.document from <http://yyyy.com>. So basically I cannot find a way to do anything with the iframe
HP
I guess if you set document.style.visibility = none (or however you can hide all the iframe contents) on the stuff inside the iframe, you still end up with a big white square? In that case you probably do need some cross-frame communication mechanism as Matthew Flaschen suggests. At the same time you inject the iframe itself, you also inject the script to handle one side of the communication; and the page loaded inside the iframe has the script to handle the other side.
Bruce