views:

623

answers:

2

I'm attempting to create an <iframe> using JavaScript, then append a <script> element to that <iframe>, which I want to run in the context of the <iframe>d document.

Unfortunately, it seems I'm doing something wrong - my JavaScript appears to execute successfully, but the context of the <script> is the parent page, not the <iframe>d document. I also get a 301 Error in Firebug's "Net" tab when the browser requests iframe_test.js, though it then requests it again (not sure why?) successfully.

This is the code I'm using (live demo at http://onespot.wsj.com/static/iframe_test.html):

iframe_test.html

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"&gt;
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title>&lt;iframe&gt; test</title>
  </head>
  <body>
    <div id="bucket"></div>
    <script type="text/javascript" src="http://jqueryjs.googlecode.com/files/jquery-1.3.2.min.js"&gt;&lt;/script&gt;
    <script type="text/javascript">
      $(document).ready(function() {
        $('#bucket').append('<iframe id="test"></iframe>');
        setTimeout(function() {
          var iframe_body = $('#test').contents().find('body');
          iframe_body.append('<scr' + 'ipt type="text/javascript" src="http://onespot.wsj.com/static/iframe_test.js"&gt;&lt;/scr' + 'ipt>');
        }, 100);
      });
    </script>
  </body>
</html>

iframe_test.js

$(function() {
  var test = '<p>Shouldn\'t this be inside the &lt;iframe&gt;?</p>';
  $('body').append(test);
});

One thing that seems unusual is that the the code in iframe_test.js even works; I haven't loaded jQuery in the <iframe> itself, only in the parent document. That seems like a clue to me, but I can't figure out what it means.

Any ideas, suggestions, etc. would be much appreciated!

A: 

I don't have time to fully debug is right now, but from a quick look on Firefox (development build) it looks like jquery is acting up:

document.documentElement.innerHTML
// gives the document source without the <script src>

document.documentElement.
  getElementsByTagName("iframe")[0].contentDocument.documentElement.innerHTML

// gives: <head><title></title></head><body></body>

A very interesting question, thanks!

Nickolay
Hmm, that's interesting, Nickolay. Thanks very much for the help troubleshooting. I wonder if the code I wrote is technically valid, but just doesn't execute in the way that I expect/hope?
Bungle
+1  A: 

I didn't find an answer to my original question, but I did find another approach that works even better (at least for my purposes).

This doesn't use jQuery on the parent page (which is actually a good thing, as I'd prefer not to load it there), but it does load jQuery in the <iframe> in an apparently completely valid and usable way. All I'm doing is writing over the <iframe>'s document object with a new one created from scratch. This allows me to simply include a <script> element in a string which I then write to the <iframe>'s document object.

The code:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"&gt;
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title>frame</title>
  </head>
  <body>

    <div id="test"></div>

    <script type="text/javascript">
      // create a new <iframe> element
      var iframe = document.createElement('iframe');

      // append the new element to the <div id="bucket"></div>
      var bucket = document.getElementById('test');
      bucket.appendChild(iframe);

      // create a string to use as a new document object
      var val = '<scr' + 'ipt type="text/javascript" src="http://jqueryjs.googlecode.com/files/jquery-1.3.2.min.js"&gt;&lt;/scr' + 'ipt>';
      val += '<scr' + 'ipt type="text/javascript"> $(function() { $("body").append("<h1>It works!</h1>"); }); </scr' + 'ipt>';

      // get a handle on the <iframe>d document (in a cross-browser way)
      var doc = iframe.contentWindow || iframe.contentDocument;
      if (doc.document) {
        doc = doc.document;
      }

      // open, write content to, and close the document
      doc.open();
      doc.write(val);
      doc.close();
    </script>

  </body>
</html>

I hope this helps someone down the road!

Bungle
This is exactly what I'm looking for. Is there any way to APPEND to the iframe so the body contents are not erased?
AnApprentice