Can scripts be inserted with innerHTML?

248,789

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
Share:
248,789

Related videos on Youtube

Craig
Author by

Craig

I like to make things simpler.

Updated on February 14, 2022

Comments

  • Craig
    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 with innerHTML?

    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
    Craig almost 15 years
    What 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
    Ariel Popovsky almost 15 years
    As 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
    zombat almost 15 years
    Ariel is right. I appreciate trying to keep your code short, and adding a <script> tag with innerHTML might be short, but it doesn't work. It's all just plain text until it gets run through eval(). And sadly, eval() doesn't parse HTML tags, so you end up with a chain of problems.
  • Eli Grey
    Eli Grey over 12 years
    Unless there aren't any other script elements in the document. Use document.documentElement instead.
  • Pablo Moretti
    Pablo Moretti over 12 years
    Isn'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
    Eli Grey over 12 years
    Who 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
    buley over 12 years
    eval() is not a great solution to any problem.
  • Wichert Akkerman
    Wichert Akkerman over 12 years
    For what it's worth: this does not appear to work on current browsers.
  • davidmh
    davidmh about 10 years
    I'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
    gsinha almost 10 years
    Thanks. It fixed my problem of adding Disqus Universal code to a modal popup created using TinyBox2 Jquery plugin.
  • Oliver
    Oliver almost 10 years
    Does 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
    geoyws about 9 years
  • Jose Gómez
    Jose Gómez almost 9 years
    Unfortunately, this solution does not work when the script contains functions that will be invoked later on.
  • Youstay Igo
    Youstay Igo over 8 years
    I 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
    Youstay Igo over 8 years
    How do you people add the &lt;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
    fregante about 8 years
    Not 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
    user3526 about 8 years
    Two 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
    newshorts over 7 years
    One 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
    Danny '365CSI' Engelman over 7 years
    You could Base64 encode your trigger-image as <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAA‌​AAALAAAAAABAAEAAAIBR‌​AA7"> (this will not do a network request) Actually... you do NOT need an image, reference a non-existing image and instead of onload use onerror (but this will do a network request)
  • Stavm
    Stavm over 7 years
    a 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
    StanE over 7 years
    user447963 & 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
    Bao Thai about 7 years
    what is the purpoes of [0]? can you use nodeScriptReplace(document.getElementById().html);
  • iPherian
    iPherian about 7 years
    I just tried it and it works on chrome 57. innerHTML on a script tag executes the text.
  • JayArby
    JayArby about 7 years
    That's interesting, it did not to work before. I wonder if this behavior is cross-browser or only in chrome 57.
  • mjs
    mjs almost 7 years
    @BaoThai Yes. You can.
  • Dave
    Dave over 6 years
    Does 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
    Soul about 6 years
    This doesn't work with Microsoft Edge, any other workaround?
  • Ivan
    Ivan over 5 years
    Easiest 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
    gabriel garcia over 5 years
    Thanks for downvoting me without saying why. Love u all.
  • Barrosy
    Barrosy about 5 years
    @buley Why is it not?
  • buley
    buley about 5 years
    @Barrosy often but not always "evil" stackoverflow.com/questions/197769/…
  • gabriel garcia
    gabriel garcia almost 5 years
    You 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
    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
    pixelherodev almost 5 years
    re: 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
    gabriel garcia almost 5 years
    u're right... I've missridden it. My apologies aswell.
  • pixelherodev
    pixelherodev almost 5 years
    @gabrielgarcia Not a problem :)
  • Kevin B
    Kevin B almost 5 years
    I mean... Appending a script tag with code content is an eval, is it not?
  • colxi
    colxi almost 5 years
    @KevinB There are notorious differences ... try eval('console.log(this)') and you will see the most obvious one
  • Kevin B
    Kevin B almost 5 years
    so the context is different, and? it’s still just an eval.
  • colxi
    colxi almost 5 years
    @KevinB No it's not an eval. Try this eval('let b=100') .. and then try to access b from outside the eval .... good luck with it, you are going to need it
  • Bezzzo
    Bezzzo about 4 years
    Works for me. Cheers
  • FZs
    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
    Martin almost 4 years
    Your newScript element has the HTMLScriptElement interface so you can just set the inline code with newScript.text = "alert('Hello World!')"; no need to create and append a new text node.
  • Jan Turoň
    Jan Turoň over 3 years
    Explanation: 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
    Danny '365CSI' Engelman over 3 years
    tnx, 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
    mjs over 3 years
    @Dave you sure you have the proper attributes on it ? rel='text/javascritp' for instance?
  • Katharina Schreiber
    Katharina Schreiber over 3 years
    I get an erro Uncaught (in promise) TypeError: Cannot read property 'length' of undefined , but I am a beginner. What am I doing wrong?
  • mjs
    mjs over 3 years
    @KatharinaSchreiber there is no promises in this code, so probably not related to this.
  • daniellalasa
    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
    Stefan Reich over 2 years
    This is freaking amazing. Works perfectly
  • Johannes Ewald
    Johannes Ewald over 2 years
    Awesome solution, thank you :) In case anyone is looking for a more modern version of this: stackoverflow.com/a/69190644/1343851
  • Jintor
    Jintor over 2 years
    wow, it does work well
  • Dave Guerrero
    Dave Guerrero over 2 years
    This is so awesome. Thank you.
  • Yash Vekaria
    Yash Vekaria over 2 years
    you can use this nice react component - github.com/christo-pr/dangerously-set-html-content
  • Jesse
    Jesse over 2 years
    This worked perfectly and is easily the simplest solution here. I don't understand why it's not higher-voted.
  • Tilak Madichetti
    Tilak Madichetti over 2 years
    its a relatively new solution to an old problem :P
  • eeerrrttt
    eeerrrttt about 2 years
    this does not run the script.
  • Herbert Van-Vliet
    Herbert Van-Vliet about 2 years
    Beautiful and complete. I like how the script element ends up inside containerElement.
  • Vael Victus
    Vael Victus about 2 years
    Simple 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
    lionelbrits about 2 years
    In 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
    Mani almost 2 years
    I'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 ?