The beautiful part of JavaScript is that it is ridiculously flexible and lets you do things that ‘probably’ shouldn’t be done. Here’s a good example of that.

During uComponents development I stumbled upon a situation where I needed to attach a JavaScript method to the top-level frame from inside of an iframe. Well in fact it turns out this is quite easy, you can do something like this:

window.top.doThis = function() { alert("woot!"); }

However, since we’re attaching the ‘doThis’ method to the main frame from an inner iframe, when the inner iframenavigates to another page, this function will no longer exist on the main frame… So this clearly isn’t going to work if we want to be able to call the ‘doThis’ method from the inner frame no matter when and where it navigates to… Conundrum!

So the next possibility is to try to just inject a script block into the main frame from the iframewhich actually does work in Firefox and Chrome but fails in Internet Explorer and Safari. (This snippet of code requires that you have jQuery loaded in the main frame)

var js = "function doThis() { alert('woot!'); }"; var injectScript = window.top.$('<script>') .attr('type', 'text/javascript') .html(js); window.top.$("head").append(injectScript);

In the above, we’re creating a string function, creating a <script> block with jQuery, appending the string function to the script block and then appending this script block to the <head> element of the main frame. But as i said before, Firefox and Chrome are ok with this but Internet Explorer and Safari will throw JavaScript exceptions such as: Unexpected call to method or property access

Ok, so unless you don’t want to be cross browser, this isn’t going to work. It took me a while to figure out that you can do this, but this does work. Yes it looks pretty dodgy, and it probably is. In reality, attempting to do something like this is pretty dodgy to begin with. So here it is (this works in Internet Explorer 8 (probably earlier ones too), Firefox 3.6 (probably earlier ones too) and Chrome 5 (probably earlier ones too) and i didn’t get around to testing Safari but I am assuming it works):

var iframe = window.top.$("#dummyIFrame"); if (iframe.length == 0) { var html = "<html><head><script type='text/javascript'>" + "this.window.doThis = function() { alert('woot'); };" + "</script></head><body></body></html>"; iframe = window.top.$("<iframe id='dummyIFrame'>") .append(html) .hide() .css("width", "0px") .css("height", "0px"); window.top.$("body").append(iframe); }

So i guess this requires a bit of explanation. All browsers seem to let you create iframes dynamically which also means that you can put whatever content into the iframes while it’s being created, including script blocks. Here’s what we’re doing:

  • checking if our ‘dummy’ iframe already exists (since we don’t want to create multiple dummy iframes since we only need one), if it doesn't:
  • create an html text block including the script that will attach the ‘doThis’ method to the ‘this.window’ object (which for some reason will be referring to the window.top object)
  • next we create an iframe element and append the html text block, and then make sure the iframe is completely hidden
  • finally, we append the iframe to the main window’s body element
window.top.doThis();

Nice! So this pretty much means that you can create code from an inner frame and attach it to a different frame, then have that code run in the context of the main frame with the main frame’s objects and script set.

One last thing that i found out you can do, though i wouldn’t recommend it because i think it might start filling up your memory. But this is also possible:

var html = "<html><head><script type='text/javascript'>" + "this.window.doThis = function() { alert('woot'); };" + "this.window.doThis();" + "</script></head><body></body></html>"; iframe = window.top.$("<iframe id='dummyIFrame'>") .append(html);

All that is happening here is that I’m attaching the ‘doThis’ method to the main frame’s window object, calling it directly after and then creating an iframe in memory with this script block. The funny part is that the method executes straight away and I haven’t attached the iframe to the DOM anywhere!