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