RidingTheClutch.com

Want to use Prototype to access something in an iframe?

At work I’m putting together a prototype that lets you live-preview style changes on a webpage (similar to Wufoo’s Theme Builder). I wanted to split a page and have your styles/themes in the top half and a preview of the site in the bottom half. Rather than try to recreate the entire site in a special “preview” mode I wanted to just show the actual site and use Javascript to change styles on the fly. A good ol’ iframe to the rescue!

You can target a document in an iframe and access it’s DOM assuming that both the container page and iframed page are both coming from the same domain. Otherwise you’ve got a security problem. The problem is that, by default, the awesome Prototype library can’t access anything in the iframe (at least not by using the familiar, standard Prototype syntax like $()).

After a little searching I found a code snippet someone put online in Prototype’s Google Group page and it works great! It adds the $() method to iframe Elements so they can call $() on themselves and search inside for a matching object (iframes respond to a call to the .contentWindow property [returns the file that’s loaded in the iframe] which is how this code determines what Elements on the page are iframes).

To implement simply include this code somewhere after your prototype.js include:

Element.addMethods('iframe', {
document: function(element) {
  element = $(element);
  if (element.contentWindow)
      return element.contentWindow.document;
  else if (element.contentDocument)
      return element.contentDocument;
  else
      return null;
},
$: function(element, frameElement) { 
  element = $(element);
  var frameDocument = element.document();
  if (arguments.length > 2) {
      for (var i = 1, frameElements = [], length = arguments.length; i < length; i++)
          frameElements.push(element.$(arguments[i]));
      return frameElements;
  }
  if (Object.isString(frameElement))
      frameElement = frameDocument.getElementById(frameElement);
  return frameElement || element;
}
});

And let’s say your page looks something like:

-- index.html
<html>
...
<iframe id="my_frame" src="contents.html" />
...
</html>

-- contents.html
<html>
...
<h1 id="logo">Hello World Industries</h1>
...
</html>

And the usage is thusly (this goes in index.html):

var iframe = $('my_frame');
var the_logo = iframe.$('logo');

Now the_logo contains a standard Prototype Element for the logo inside the iframe!

Bonus Need to target the body itself?

var iframe = $('my_frame');
var iframe_body = iframe.document().body;
iframe_body.setStyle({backgroundColor:'#990000'});   // set the background to dark red
blog comments powered by Disqus