Can scripts be inserted with innerHTML?
Solution 1
You have to use eval() to execute any script code that you've inserted as DOM text.
MooTools will do this for you automatically, and I'm sure jQuery would as well (depending on the version. jQuery version 1.6+ uses eval
). This saves a lot of hassle of parsing out <script>
tags and escaping your content, as well as a bunch of other "gotchas".
Generally if you're going to eval()
it yourself, you want to create/send the script code without any HTML markup such as <script>
, as these will not eval()
properly.
Solution 2
Here is a method that recursively replaces all scripts with executable ones:
function nodeScriptReplace(node) {
if ( nodeScriptIs(node) === true ) {
node.parentNode.replaceChild( nodeScriptClone(node) , node );
}
else {
var i = -1, children = node.childNodes;
while ( ++i < children.length ) {
nodeScriptReplace( children[i] );
}
}
return node;
}
function nodeScriptClone(node){
var script = document.createElement("script");
script.text = node.innerHTML;
var i = -1, attrs = node.attributes, attr;
while ( ++i < attrs.length ) {
script.setAttribute( (attr = attrs[i]).name, attr.value );
}
return script;
}
function nodeScriptIs(node) {
return node.tagName === 'SCRIPT';
}
Example call:
nodeScriptReplace(document.getElementsByTagName("body")[0]);
Solution 3
Here is a very interesting solution to your problem: http://24ways.org/2005/have-your-dom-and-script-it-too
So use this instead of script tags:
<img src="empty.gif" onload="alert('test');this.parentNode.removeChild(this);" />
Solution 4
You can create script and then inject the content.
var g = document.createElement('script');
var s = document.getElementsByTagName('script')[0];
g.text = "alert(\"hi\");"
s.parentNode.insertBefore(g, s);
This works in all browsers :)
Solution 5
I used this code, it is working fine
var arr = MyDiv.getElementsByTagName('script')
for (var n = 0; n < arr.length; n++)
eval(arr[n].innerHTML)//run script inside div
Related videos on Youtube
Comments
-
Craig about 2 years
I tried to load some scripts into a page using
innerHTML
on a<div>
. It appears that the script loads into the DOM, but it is never executed (at least in Firefox and Chrome). Is there a way to have scripts execute when inserting them withinnerHTML
?Sample code:
<!DOCTYPE html> <html> <body onload="document.getElementById('loader').innerHTML = '<script>alert(\'hi\')<\/script>'"> Shouldn't an alert saying 'hi' appear? <div id="loader"></div> </body> </html>
-
Craig almost 15 yearsWhat I really want to do is to load an external script, not just eval some local script. Adding a script tag with innerHTML is much shorter than creating a script DOM element and adding it to the body, and I am trying to make my code as short as possible. Do you have to create the dom script elements and add them to the dom rather than just using something like innerHTML? Is there a way to do this with document.write from within a function?
-
Ariel Popovsky almost 15 yearsAs zombat suggest, use a Javascript framework to load the external script, don't try to reinvent the wheel. JQuery makes this extremely easy, just include JQuery and call: $.getScript(url). You can also provide a callback function that will get executed once the script is loaded.
-
zombat almost 15 yearsAriel is right. I appreciate trying to keep your code short, and adding a
<script>
tag withinnerHTML
might be short, but it doesn't work. It's all just plain text until it gets run througheval()
. And sadly,eval()
doesn't parse HTML tags, so you end up with a chain of problems. -
Eli Grey over 12 yearsUnless there aren't any other script elements in the document. Use
document.documentElement
instead. -
Pablo Moretti over 12 yearsIsn't necessary because you are writing a script from another script.
<script>
var g = document.createElement('script');
var s = document.getElementsByTagName('script')[0]; //reference this script
g.text = "alert(\"hi\");"
s.parentNode.insertBefore(g, s);
</script>
-
Eli Grey over 12 yearsWho says it's from another script? You can run JavaScript without
<script>
elements. E.g.<img onerror="..." src="#">
and<body onload="...">
. If you want to be technical, this won't work in non-HTML/SVG documents either due to the inexplicit namespacing. -
buley over 12 yearseval() is not a great solution to any problem.
-
Wichert Akkerman over 12 yearsFor what it's worth: this does not appear to work on current browsers.
-
davidmh about 10 yearsI'm a bit surprised that your answer it's all the way down. IMHO, this is the best solution, this method would even allow you to restrict scripts with specific urls or content.
-
gsinha almost 10 yearsThanks. It fixed my problem of adding Disqus Universal code to a modal popup created using TinyBox2 Jquery plugin.
-
Oliver almost 10 yearsDoes not work for me, when inserted into a result of an ajax request : Syntax error missing ; before statement at the start of the script string
-
geoyws about 9 yearsFacebook uses Pablo's answer in their SDK. developers.facebook.com/docs/javascript/quickstart/v2.2#loading
-
Jose Gómez almost 9 yearsUnfortunately, this solution does not work when the script contains functions that will be invoked later on.
-
Youstay Igo over 8 yearsI tried eval() myself. It is a horrible idea. You have to eval the whole thing EACH TIME. Even if you declare a variable name and value, you have to re-declare/re-eval() it every time afresh to make it work. It's a nightmare of errors.
-
Youstay Igo over 8 yearsHow do you people add the <img src... line to your page code? Do you document.write() it or use document.body.innerHTML+= approach for that? Both are failing for me :(
-
fregante about 8 yearsNot very practical to write a lot of code inside an
onload
attribute. Also this requires an additional file to exist and to be loaded. momo's solution is less of a compromise. -
user3526 about 8 yearsTwo drawbacks: 1. It cannot call any scripts/functions added through the innerHTML(along with this IMG tag) because they dont exist as far as the browser is concerned 2. If part of the inline code before ".removeChild()" throws an exception, the img element will not be removed.
-
newshorts over 7 yearsOne quick point. This solution may not give you accurate results, if you are loading larger images above it (since they may take longer to download than your empty gif).
-
Danny '365CSI' Engelman over 7 yearsYou could Base64 encode your trigger-image as
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7">
(this will not do a network request) Actually... you do NOT need an image, reference a non-existing image and instead ofonload
useonerror
(but this will do a network request) -
Stavm over 7 yearsa bit late here, but anyone that may be using this method, notice that in JQuery you need to load your scripts using $.loadScript(url) rather than <script src="url></script> - the latter will cause a deprecated Synchronous XMLHttpRequest error on browsers.
-
StanE over 7 yearsuser447963 & Danny'365CSI'Engelman: This is really brilliant. This is what maybe might be called hacking. Thinking differently to achieve something that would not be possible otherwise. A thing you do not normally think of (although you know of it somewhere in your head).
-
Bao Thai about 7 yearswhat is the purpoes of [0]? can you use nodeScriptReplace(document.getElementById().html);
-
iPherian about 7 yearsI just tried it and it works on chrome 57. innerHTML on a script tag executes the text.
-
JayArby about 7 yearsThat's interesting, it did not to work before. I wonder if this behavior is cross-browser or only in chrome 57.
-
mjs almost 7 years@BaoThai Yes. You can.
-
Dave over 6 yearsDoes not seem to help in IWebBrowser2; I can confirm the script tags get recreated with createElement, but I'm still unable to invoke them via InvokeScript().
-
Soul about 6 yearsThis doesn't work with Microsoft Edge, any other workaround?
-
Ivan over 5 yearsEasiest to implement and works in all major browsers (tested in IE, Edge, Opera, Firefox & Chrome latest versions). Thinking outside the box does have it's benefits!
-
gabriel garcia over 5 yearsThanks for downvoting me without saying why. Love u all.
-
Barrosy about 5 years@buley Why is it not?
-
buley about 5 years@Barrosy often but not always "evil" stackoverflow.com/questions/197769/…
-
gabriel garcia almost 5 yearsYou do notice that u're adding a new
MutationObserver
each time a element is appended to the document, right? Btw, I wonder why do you say my code is not functional. -
pixelherodev almost 5 years@gabrielgarcia I said your code wasn't functional because I tried it and it simply didn't work. Looking at it now, it's entirely possible that was on me, not you, and I sincerely apologize for the way I phrased this. Fixing it now.
-
pixelherodev almost 5 yearsre: adding a MutationObserver each time an element is added to the document, what are you talking about? DOMContentLoaded, and I quote from MDN here,"fires when the initial HTML document has been completely loaded and parsed, without waiting for stylesheets, images, and subframes to finish loading." That's once, and once only. Furthermore, this script is working without issues on my site, and debugging shows it only happening once, so it's once in practice as well as in theory.
-
gabriel garcia almost 5 yearsu're right... I've missridden it. My apologies aswell.
-
pixelherodev almost 5 years@gabrielgarcia Not a problem :)
-
Kevin B almost 5 yearsI mean... Appending a script tag with code content is an eval, is it not?
-
colxi almost 5 years@KevinB There are notorious differences ... try
eval('console.log(this)')
and you will see the most obvious one -
Kevin B almost 5 yearsso the context is different, and? it’s still just an eval.
-
colxi almost 5 years@KevinB No it's not an eval. Try this
eval('let b=100')
.. and then try to accessb
from outside the eval .... good luck with it, you are going to need it -
Bezzzo about 4 yearsWorks for me. Cheers
-
FZs about 4 years@Danny'365CSI'Engelman Oooooor, write something invalid to
src
: will trigger onerror but won't make a network request:src="?" onerror="alert('Hello');this.parentNode.removeChild(this)"
-
Martin almost 4 yearsYour
newScript
element has the HTMLScriptElement interface so you can just set the inline code withnewScript.text = "alert('Hello World!')";
no need to create and append a new text node. -
Jan Turoň over 3 yearsExplanation: this snippet (added to HTML directly) ensures that any future external script added as innerHTML will be parsed. I find it as a nice universal idea, but I didn't upvote due to a concern if scripts added by
document.createElement
could be possibly executed twice? It also scares me to change the mutated object - I am not sure if it couldn't produce an infinite loop in some cases. -
Danny '365CSI' Engelman over 3 yearstnx, that was a 2016 answer. I would now do:
<img src="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'/>" onload="console.log(21,this)"/>
-
mjs over 3 years@Dave you sure you have the proper attributes on it ? rel='text/javascritp' for instance?
-
Katharina Schreiber over 3 yearsI get an erro Uncaught (in promise) TypeError: Cannot read property 'length' of undefined , but I am a beginner. What am I doing wrong?
-
mjs over 3 years@KatharinaSchreiber there is no promises in this code, so probably not related to this.
-
daniellalasa about 3 years@Martin Sure, There's lots of different ways in programming world to implement stuffs! also that's more clear and maintainable :).
-
Stefan Reich over 2 yearsThis is freaking amazing. Works perfectly
-
Johannes Ewald over 2 yearsAwesome solution, thank you :) In case anyone is looking for a more modern version of this: stackoverflow.com/a/69190644/1343851
-
Jintor over 2 yearswow, it does work well
-
Dave Guerrero over 2 yearsThis is so awesome. Thank you.
-
Yash Vekaria over 2 yearsyou can use this nice react component - github.com/christo-pr/dangerously-set-html-content
-
Jesse over 2 yearsThis worked perfectly and is easily the simplest solution here. I don't understand why it's not higher-voted.
-
Tilak Madichetti over 2 yearsits a relatively new solution to an old problem :P
-
eeerrrttt about 2 yearsthis does not run the script.
-
Herbert Van-Vliet about 2 yearsBeautiful and complete. I like how the script element ends up inside containerElement.
-
Vael Victus about 2 yearsSimple and concise when you trust your own input. Here's my implementation:
[...document.querySelector('#content').children].filter(x => x.nodeName === 'SCRIPT').forEach(x => eval(x.innerText));
-
lionelbrits about 2 yearsIn my case, the added HTML contains two script tags, the first loads an external script, the second references it inline. This causes an error saying the variables exported by the external script are not defined. If I include the two script tags directly, the code works. My guess is that the first script tag isn't completely done by the time the second is executed.
-
Mani almost 2 yearsI'm so amazed by you answer. I searched for 5 hours before trying your solution. @TilakMaddy do you know any reason why people don't mention createContextualRange as solution ? is there any browser compatibility issue ?