By Craig


2009-07-29 01:01:12 8 Comments

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>

16 comments

@pixelherodev 2018-10-13 20:47:45

Gabriel Garcia's mention of MutationObservers is on the right track, but didn't quite work for me. I am not sure if that was because of a browser quirk or due to a mistake on my end, but the version that ended up working for me was the following:

document.addEventListener("DOMContentLoaded", function(event) {
    var observer = new MutationObserver(mutations=>{
        mutations.map(mutation=>{
            Array.from(mutation.addedNodes).map(node=>{
                if (node.tagName === "SCRIPT") {
                    var s = document.createElement("script");
                    s.text=node.text;
                    if (typeof(node.parentElement.added) === 'undefined')
                        node.parentElement.added = [];
                    node.parentElement.added[node.parentElement.added.length] = s;
                    node.parentElement.removeChild(node);
                    document.head.appendChild(s);
                }
            })
        })
    })
    observer.observe(document.getElementById("element_to_watch"), {childList: true, subtree: true,attributes: false});
};

Of course, you should replace element_to_watch with the name of the element that is being modified.

node.parentElement.added is used to store the script tags that are added to document.head. In the function used to load the external page, you can use something like the following to remove no longer relevant script tags:

function freeScripts(node){
    if (node === null)
        return;
    if (typeof(node.added) === 'object') {
        for (var script in node.added) {
            document.head.removeChild(node.added[script]);
        }
        node.added = {};
    }
    for (var child in node.children) {
        freeScripts(node.children[child]);
    }
}

And an example of the beginning of a load function:

function load(url, id, replace) {
    if (document.getElementById(id) === null) {
        console.error("Element of ID "+id + " does not exist!");
        return;
    }
    freeScripts(document.getElementById(id));
    var xhttp = new XMLHttpRequest();
    // proceed to load in the page and modify innerHTML
}

@gabriel garcia 2019-06-14 16:55:25

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 2019-06-20 03:16:33

@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 2019-06-20 03:19:06

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 2019-06-21 14:07:01

u're right... I've missridden it. My apologies aswell.

@pixelherodev 2019-06-23 06:02:19

@gabrielgarcia Not a problem :)

@gabriel garcia 2017-10-06 13:34:52

My solution for this problem is to set a Mutation Observer to detect <script></script> nodes and then replace it with a new <script></script> node with the same src. For example:

let parentNode = /* node to observe */ void 0
let observer = new MutationObserver(mutations=>{
    mutations.map(mutation=>{
        Array.from(mutation.addedNodes).map(node=>{
            if ( node.parentNode == parentNode ) {
                let scripts = node.getElementsByTagName('script')
                Array.from(scripts).map(script=>{
                    let src = script.src
                    script = document.createElement('script')
                    script.src = src
                    return script
                })
            }
        })
    })
})
observer.observe(document.body, {childList: true, subtree: true});

@gabriel garcia 2018-11-16 15:23:18

Thanks for downvoting me without saying why. Love u all.

@colxi 2019-05-22 02:34:03

Here a solution that does not use eval, and works with scripts, linked scripts , as well as with modules.

The function accepts 3 parameters :

  • html : String with the html code to insert
  • dest : reference to the target element
  • append : boolean flag to enable appending at the end of the target element html
function insertHTML(html, dest, append=false){
    // if no append is requested, clear the target element
    if(!append) dest.innerHTML = '';
    // create a temporary container and insert provided HTML code
    let container = document.createElement('div');
    container.innerHTML = html;
    // cache a reference to all the scripts in the container
    let scripts = container.querySelectorAll('script');
    // get all child elements and clone them in the target element
    let nodes = container.childNodes;
    for( let i=0; i< nodes.length; i++) dest.appendChild( nodes[i].cloneNode(true) );
    // force the found scripts to execute...
    for( let i=0; i< scripts.length; i++){
        let script = document.createElement('script');
        script.type = scripts[i].type || 'text/javascript';
        if( scripts[i].hasAttribute('src') ) script.src = scripts[i].src;
        script.innerHTML = scripts[i].innerHTML;
        document.head.appendChild(script);
        document.head.removeChild(script);
    }
    // done!
    return true;
}

@Jake 2018-06-26 16:34:20

You could do it like this:

var mydiv = document.getElementById("mydiv");
var content = "<script>alert(\"hi\");<\/script>";

mydiv.innerHTML = content;
var scripts = mydiv.getElementsByTagName("script");
for (var i = 0; i < scripts.length; i++) {
    eval(scripts[i].innerText);
}

@Gray 2018-02-09 16:44:05

Try using template and document.importNode. Here is an example:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Sample</title>
</head>
<body>
<h1 id="hello_world">Sample</h1>
<script type="text/javascript">
 var div = document.createElement("div");
  var t = document.createElement('template');
  t.innerHTML =  "Check Console tab for javascript output: Hello world!!!<br/><script type='text/javascript' >console.log('Hello world!!!');<\/script>";
  
  for (var i=0; i < t.content.childNodes.length; i++){
    var node = document.importNode(t.content.childNodes[i], true);
    div.appendChild(node);
  }
 document.body.appendChild(div);
</script>
 
</body>
</html>

@Soul 2018-03-23 00:24:22

This doesn't work with Microsoft Edge, any other workaround?

@Trevor Elliott 2018-11-16 18:20:38

This doesn't work in Chrome either.

@huseyin tugrul buyukisik 2019-01-17 00:51:30

Firefox: doesn't work.

@Adnan Korkmaz 2017-05-02 08:08:59

Here is a recursive function to set the innerHTML of an element that I use in our ad server:

// o: container to set the innerHTML
// html: html text to set.
// clear: if true, the container is cleared first (children removed)
function setHTML(o, html, clear) {
    if (clear) o.innerHTML = "";

    // Generate a parseable object with the html:
    var dv = document.createElement("div");
    dv.innerHTML = html;

    // Handle edge case where innerHTML contains no tags, just text:
    if (dv.children.length===0){ o.innerHTML = html; return; }

    for (var i = 0; i < dv.children.length; i++) {
        var c = dv.children[i];

        // n: new node with the same type as c
        var n = document.createElement(c.nodeName);

        // copy all attributes from c to n
        for (var j = 0; j < c.attributes.length; j++)
            n.setAttribute(c.attributes[j].nodeName, c.attributes[j].nodeValue);

        // If current node is a leaf, just copy the appropriate property (text or innerHTML)
        if (c.children.length == 0)
        {
            switch (c.nodeName)
            {
                case "SCRIPT":
                    if (c.text) n.text = c.text;
                    break;
                default:
                    if (c.innerHTML) n.innerHTML = c.innerHTML;
                    break;
            }
        }
        // If current node has sub nodes, call itself recursively:
        else setHTML(n, c.innerHTML, false);
        o.appendChild(n);
    }
}

You can see the demo here.

@JayArby 2016-11-04 15:21:11

For anyone still trying to do this, no, you can't inject a script using innerHTML, but it is possible to load a string into a script tag using a Blob and URL.createObjectURL.

I've created an example that lets you run a string as a script and get the 'exports' of the script returned through a promise:

function loadScript(scriptContent, moduleId) {
    // create the script tag
    var scriptElement = document.createElement('SCRIPT');

    // create a promise which will resolve to the script's 'exports'
    // (i.e., the value returned by the script)
    var promise = new Promise(function(resolve) {
        scriptElement.onload = function() {
            var exports = window["__loadScript_exports_" + moduleId];
            delete window["__loadScript_exports_" + moduleId];
            resolve(exports);
        }
    });

    // wrap the script contents to expose exports through a special property
    // the promise will access the exports this way
    var wrappedScriptContent =
        "(function() { window['__loadScript_exports_" + moduleId + "'] = " + 
        scriptContent + "})()";

    // create a blob from the wrapped script content
    var scriptBlob = new Blob([wrappedScriptContent], {type: 'text/javascript'});

    // set the id attribute
    scriptElement.id = "__loadScript_module_" + moduleId;

    // set the src attribute to the blob's object url 
    // (this is the part that makes it work)
    scriptElement.src = URL.createObjectURL(scriptBlob);

    // append the script element
    document.body.appendChild(scriptElement);

    // return the promise, which will resolve to the script's exports
    return promise;
}

...

function doTheThing() {
    // no evals
    loadScript('5 + 5').then(function(exports) {
         // should log 10
        console.log(exports)
    });
}

I've simplified this from my actual implementation, so no promises that there aren't any bugs in it. But the principle works.

If you don't care about getting any value back after the script runs, it's even easier; just leave out the Promise and onload bits. You don't even need to wrap the script or create the global window.__load_script_exports_ property.

@iPherian 2017-04-22 06:35:22

I just tried it and it works on chrome 57. innerHTML on a script tag executes the text.

@JayArby 2017-05-02 21:51:49

That's interesting, it did not to work before. I wonder if this behavior is cross-browser or only in chrome 57.

@iirekm 2014-05-07 08:27:04

Use $(parent).html(code) instead of parent.innerHTML = code.

The following also fixes scripts that use document.write and scripts loaded via src attribute. Unfortunately even this doesn't work with Google AdSense scripts.

var oldDocumentWrite = document.write;
var oldDocumentWriteln = document.writeln;
try {
    document.write = function(code) {
        $(parent).append(code);
    }
    document.writeln = function(code) {
        document.write(code + "<br/>");
    }
    $(parent).html(html); 
} finally {
    $(window).load(function() {
        document.write = oldDocumentWrite
        document.writeln = oldDocumentWriteln
    })
}

Source

@Stavm 2016-11-23 13:57:51

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.

@momomo 2013-12-14 14:20:25

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        = 0;
                var children = node.childNodes;
                while ( i < children.length ) {
                        nodeScriptReplace( children[i++] );
                }
        }

        return node;
}
function nodeScriptIs(node) {
        return node.tagName === 'SCRIPT';
}
function nodeScriptClone(node){
        var script  = document.createElement("script");
        script.text = node.innerHTML;
        for( var i = node.attributes.length-1; i >= 0; i-- ) {
                script.setAttribute( node.attributes[i].name, node.attributes[i].value );
        }
        return script;
}

Example call:

nodeScriptReplace(document.getElementsByTagName("body")[0]);

@davidmh 2014-04-02 21:55:14

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.

@inf3rno 2015-06-19 08:53:26

does now work in firefox

@momomo 2015-06-19 09:36:37

@inf3rno does or doesn't? It used to work, anybody ever claimed anything different?

@Bao Thai 2017-04-21 01:17:28

what is the purpoes of [0]? can you use nodeScriptReplace(document.getElementById().html);

@momomo 2017-08-04 22:03:59

@BaoThai Yes. You can.

@Dave 2017-12-04 01:43:38

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().

@user3198805 2014-01-28 16:22:41

Krasimir Tsonev has a great solution that overcome all problems. His method doesn't need using eval, so no performance nor security problems exist. It allows you to set innerHTML string contains html with js and translate it immediately to an DOM element while also executes the js parts exist along the code. short ,simple, and works exactly as you want.

Enjoy his solution:

http://krasimirtsonev.com/blog/article/Convert-HTML-string-to-DOM-element

Important notes:

  1. You need to wrap the target element with div tag
  2. You need to wrap the src string with div tag.
  3. If you write the src string directly and it includes js parts, please take attention to write the closing script tags correctly (with \ before /) as this is a string.

@Firas Nizam 2013-04-29 12:04:56

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

@gsinha 2014-07-06 01:32:57

Thanks. It fixed my problem of adding Disqus Universal code to a modal popup created using TinyBox2 Jquery plugin.

@Jose Gómez 2015-07-16 01:16:07

Unfortunately, this solution does not work when the script contains functions that will be invoked later on.

@programmer5000 2018-05-02 19:20:09

this also doesn't work for external scripts

@zombat 2009-07-29 01:04:25

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.

@Craig 2009-07-29 01:23:05

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 2009-07-29 01:33:06

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 2009-07-29 01:46:16

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.

@buley 2011-10-13 15:47:05

eval() is not a great solution to any problem.

@Youstay Igo 2015-11-04 12:17:58

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.

@Barrosy 2019-04-26 08:37:17

@buley Why is it not?

@buley 2019-05-03 00:24:49

@Barrosy often but not always "evil" stackoverflow.com/questions/197769/…

@Knowledge Serve 2013-01-22 14:29:12

Execute (Java Script) tag from innerHTML

Replace your script element with div having a class attribute class="javascript" and close it with </div>

Don't change the content that you want to execute (previously it was in script tag and now it is in div tag)

Add a style in your page...

<style type="text/css"> .javascript { display: none; } </style>

Now run eval using jquery(Jquery js should be already included)

   $('.javascript').each(function() {
      eval($(this).text());

    });`

You can explore more here, at my blog.

@Pablo Moretti 2011-08-14 00:36:57

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 :)

@Eli Grey 2011-08-14 08:01:23

Unless there aren't any other script elements in the document. Use document.documentElement instead.

@Pablo Moretti 2011-08-14 09:11:09

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 2011-08-15 04:14:42

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.

@geoyws 2015-02-08 16:51:18

@huseyin tugrul buyukisik 2019-01-17 01:05:35

Only this worked. All others failed.

@user447963 2010-09-15 03:07:44

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);" />

@Jordi P.S. 2012-09-25 11:49:48

very good one, and working fully in all browsers!

@confile 2013-08-20 11:38:18

This is brilliant!

@Oliver 2014-08-03 02:30:59

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

@Youstay Igo 2015-11-04 12:18:52

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 :(

@bfred.it 2016-03-10 10:11:33

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 2016-04-12 13:03:43

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 2016-08-30 00:47:20

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 2016-10-20 08:45:51

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)

@StanE 2017-01-15 00:38:19

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).

@Ivan 2018-09-25 09:54:21

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!

@mwilcox 2009-07-29 12:23:05

Yes you can, but you have to do it outside of the DOM and the order has to be right.

var scr = '<scr'+'ipt>alert("foo")</scr'+'ipt>';
window.onload = function(){
    var n = document.createElement("div");
    n.innerHTML = scr;
    document.body.appendChild(n);
}

...will alert 'foo'. This won't work:

document.getElementById("myDiv").innerHTML = scr;

And even this won't work, because the node is inserted first:

var scr = '<scr'+'ipt>alert("foo")</scr'+'ipt>';
window.onload = function(){
    var n = document.createElement("div");
    document.body.appendChild(n);
    n.innerHTML = scr;  
}

@Wichert Akkerman 2012-01-31 10:55:43

For what it's worth: this does not appear to work on current browsers.

@jayarjo 2016-10-04 10:52:48

Which is actually a good thing I think.

Related Questions

Sponsored Content

15 Answered Questions

[SOLVED] How to insert an item into an array at a specific index (JavaScript)?

33 Answered Questions

[SOLVED] How can I upload files asynchronously?

74 Answered Questions

[SOLVED] How can I convert a string to boolean in JavaScript?

  • 2008-11-05 00:13:08
  • Kevin
  • 1674978 View
  • 2209 Score
  • 74 Answer
  • Tags:   javascript

14 Answered Questions

[SOLVED] How can I select an element by name with jQuery?

26 Answered Questions

[SOLVED] Can (a== 1 && a ==2 && a==3) ever evaluate to true?

34 Answered Questions

23 Answered Questions

[SOLVED] How can I refresh a page with jQuery?

60 Answered Questions

[SOLVED] How can I merge properties of two JavaScript objects dynamically?

73 Answered Questions

[SOLVED] How can I get query string values in JavaScript?

12 Answered Questions

[SOLVED] How to replace innerHTML of a div using jQuery?

Sponsored Content