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:
["\nElement.addMethods('iframe', {\ndocument: function(element) {\n element = $(element);\n if (element.contentWindow)\n return element.contentWindow.document;\n else if (element.contentDocument)\n return element.contentDocument;\n else\n return null;\n},\n$: function(element, frameElement) { \n element = $(element);\n var frameDocument = element.document();\n if (arguments.length > 2) {\n for (var i = 1, frameElements = [], length = arguments.length; i < length; i++)\n frameElements.push(element.$(arguments[i]));\n return frameElements;\n }\n if (Object.isString(frameElement))\n frameElement = frameDocument.getElementById(frameElement);\n return frameElement || element;\n}\n});\n"]
And let’s say your page looks something like:
["\n-- index.html\n<html>\n...\n<iframe id=\"my_frame\" src=\"contents.html\" />\n...\n</html>\n\n-- contents.html\n<html>\n...\n<h1 id=\"logo\">Hello World Industries</h1>\n...\n</html>\n"]
And the usage is thusly (this goes in index.html):
["\nvar iframe = $('my_frame');\nvar the_logo = iframe.$('logo');\n"]
Now the_logo
contains a standard Prototype Element for the logo inside the iframe!
Bonus Need to target the body itself?
["\nvar iframe = $('my_frame');\nvar iframe_body = iframe.document().body;\niframe_body.setStyle({backgroundColor:'#990000'}); // set the background to dark red\n"]