20

I want to dynamically add javascript to an existing script element something like:

var se = document.createElement('script');
se.setAttribute('type', 'text/javascript');
se.innerHTML = 'alert(1)';
document.getElementsByTagName('head').item(0).appendChild(se);

The interesting part is se.innerHTML = 'alert(1)'; and if it is valid? If not how can I do this the right way?

picknick
  • 3,897
  • 6
  • 33
  • 48
  • 2
    I tried it, after adding the ` – aularon Sep 01 '10 at 15:09

3 Answers3

24

All browsers currently support a javascript text property, and will evaluate the text when a new script element (without a src attribute) is added to the document.

innerHTML or adding child nodes to a script element do not evaluate the script in all browsers.

function addCode(code){
    var JS= document.createElement('script');
    JS.text= code;
    document.body.appendChild(JS);
}

//test case

var s= 'document.body.ondblclick=function(e){\n'+
'e=window.event? event.srcElement:e.target;\n'+
'alert(e.id || e.tagName);\n'+
'}\nalert("ready to double click!");';

addCode(s);
kennebec
  • 102,654
  • 32
  • 106
  • 127
11

That's not adding JavaScript to an existing script element, it's creating a new script element and adding it to the document.

This does work in modern browsers, but you wouldn't normally do it unless you had some code in a variable that you really needed to execute in global context (so you couldn't use new Function(), or eval from inside a function).

What's the use case? Do you really have to do this?

If you did try to change the script's content by writing to the text content of a <script> that was already in the document, it would not cause the new script content to be run, it would just change the contents of the DOM. The exact circumstances of what causes new script to be run when a <script> element is manipulated vary from browser to browser (though HTML5 is trying to standardise it); for now it is better to avoid doing anything other than simply creating and appending a new script. (And even better to avoid scripting <script> at all, if possible.)

Setting innerHTML will work; RoToRa's method with createTextNode is better though. For <script> in an old-school-HTML document, innerHTML will actually do the same thing as createTextNode, since <script> is a CDATA element which cannot contain markup. It would matter for XHTML-served-as-XML though, and in general it is cleaner to avoid innerHTML and its escaping problems when you just want to set plain text.

Also, you can use [0] instead of item(0) (this is defined as part of the JavaScript DOM bindings), and you should in general avoid getAttribute/setAttribute; use the DOM HTML properties like se.type=... instead, which are more readable and less buggy in IE (though the IE bugs wouldn't affect you for the type attribute).

bobince
  • 528,062
  • 107
  • 651
  • 834
  • +1 eval seems the logical way to do this. However, be very suspicious about evalling just anything that comes from the server. – Joeri Hendrickx Sep 01 '10 at 15:58
  • +1 for asking for the use case and the suggestions in the last paragraph. – RoToRa Sep 02 '10 at 13:59
  • +1 @bobince , related question. if the interpreter has already passed the `head` section , and later , at the end of `body` , I create new `script` element and append it to the `head` - it still runs the script ( even if async=false). which leads me to ask : does script are handled differently ? (I mean - the Head section was already digested and still the future appended script - still runs) http://jsbin.com/EraMuKE/15/edit – Royi Namir Jan 06 '14 at 10:45
  • @Royi: yes, the script gets run on injection anywhere into the DOM. (Though what counts as the active DOM is slightly different across browsers, the document `` works consistently.) There is not necessarily any coherent thinking behind why this works that way. Script insertion was not a deliberately designed feature, it's just what IE did and others copied (IE being the first browser to implement the fully-interactive DOM as we know it). – bobince Jan 06 '14 at 10:53
  • tnx What do you mean by "active dom" ? – Royi Namir Jan 06 '14 at 10:55
  • In some browsers the script only executes when inserted into a node that is a descendent of the page's document. IIRC in IE inserting into a disconnected node still executes the script. – bobince Jan 06 '14 at 12:50
4

Using innerHTML will break if the text contains anything that can be interpreted as HTML such as <. It would be better to append one (or more) text nodes:

var se = document.createElement('script');
se.setAttribute('type', 'text/javascript');
se.appendChild(document.createTextNode('alert(1)'));
document.getElementsByTagName('head').item(0).appendChild(se);
RoToRa
  • 37,635
  • 12
  • 69
  • 105