43

This link (archived version) describes how to inject code from a script into an iframe:

function injectJS() {
  var iFrameHead = window.frames["myiframe"].document.getElementsByTagName("head")[0];
  var myscript = document.createElement('script');
  myscript.type = 'text/javascript';
  myscript.src = 'myscript.js'; // replace this with your SCRIPT
  iFrameHead.appendChild(myscript);
}

That's ok, but what if I want to insert a function object into an iframe and get it executed in the iframe context? Let's say I have:

function foo () {
    console.log ("Look at me, executed inside an iframe!", window);
}

and I want to insert foo's code inside an iframe? (function foo could be something loaded dynamically, I can't just wrap it in quotes)

I naively tried:

var scriptFooString = "<script>" + foo.toString() + "</script>"

to get the code inside function, but

  • I don't know how to insert it in the iframe HEAD (maybe with jquery?)
  • I don't know if it's the right way
  • I don't know what happens when if function is way more complex than that
  • I don't know what happens with double and single quotes in scriptFooString

Any hint?

Nathan Arthur
  • 8,287
  • 7
  • 55
  • 80
janesconference
  • 6,333
  • 8
  • 55
  • 73

3 Answers3

42

First of all you can only accomplish this if your frame and the page displaying it is within the same domain (Due to cross-domain rules)

secondly you can manipulate dom and window objects of the frame directly through JS:

frames[0].window.foo = function(){
   console.log ("Look at me, executed inside an iframe!", window);
}

to get your frame from a DOMElement object you can use:

var myFrame = document.getElementById('myFrame');

myFrame.contentWindow.foo = function(){
       console.log ("Look at me, executed inside an iframe!");
}

Note that the scope in foo is NOT changed, so window is still the parent window etc. inside foo.

If you want to inject some code that needs to be run in the context of the other frame you could inject a script tag, or eval it:

frames[0].window.eval('function foo(){ console.log("Im in a frame",window); }');

Though the general consensus is to never use eval, I think its a better alternative than DOM injection if you REALLY need to accomplish this.

So in your specific case you could do something like:

frames[0].window.eval(foo.toString());
EJTH
  • 2,178
  • 1
  • 20
  • 25
  • Ok, but if I execute the function from the parent window with `frames[0].window.foo();`, it gets executed in the parent window context. Inserting it in the head would have it executed with the iframe context, am I wrong? (see http://jsfiddle.net/7rEXT/) – janesconference Apr 24 '13 at 14:38
  • The problem is not context, but scope. When you make the function foo, you make it within the current scope i believe, so window is the parent window and not the frames etc. You would still need to use `myFrame.contentWindow` or `frames[0].window` inside the function to access the correct scope. If you need to inject a script tag, you can append it through DOM. – EJTH Apr 25 '13 at 06:55
  • I have updated my answer to better match your query. Though I must say that if you need to do something like this, chances are that you are overcomplicating things, and that you are doing it wrong, or could be doing it smarter. – EJTH Apr 25 '13 at 07:13
  • 4
    I need to execute dynamically loaded plugins (with requirejs) in a sandbox. Do you know of a better way to do that? – janesconference Apr 25 '13 at 11:07
6

This code is the result of my research. The accepted answer also helped me a lot. First of all, I create a simple iframe:

<iframe id="myiframe" width="200" height="200" srcdoc="<h1 id='title'>Hello from Iframe</h1><button type='button' id='fire'>Click Me!</button>
"></iframe>

For access to iframe's window and document I used this code.

const iframe = document.getElementById('myiframe');
const iframeWin = iframe.contentWindow || iframe;
const iframeDoc = iframe.contentDocument || iframeWin.document;

Finally I injected js codes into iframe:

var script = iframeDoc.createElement("script");
  script.append(`
    window.onload = function() {
     document.getElementById("fire").addEventListener('click', function() {
        const text = document.getElementById('title').innerText;
        alert(text);
     })
  }
`);
  iframeDoc.documentElement.appendChild(script);

Demo: https://jsfiddle.net/aliam/1z8f7awk/2/

Ali Hesari
  • 1,821
  • 5
  • 25
  • 51
1

Here's my solution. I'm using jquery to insert the content, then using eval to execute the script tags in the context of that iframe:

    var content = $($.parseHTML(source, document, true));
    $("#content").contents().find("html").html(content);
    var cw = document.getElementById("content").contentWindow;
    [].forEach.call(cw.document.querySelectorAll("script"), function (el, idx) {
        cw.eval(el.textContent);
    });
frumbert
  • 2,323
  • 5
  • 30
  • 61