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:

["\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"]
blog comments powered by Disqus