Can scripts be inserted with innerHTML?
Asked Answered
I

26

294

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>
Intricate answered 29/7, 2009 at 1:1 Comment(0)
E
107

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.

Escallop answered 29/7, 2009 at 1:4 Comment(7)
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?Intricate
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.Rockafellow
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.Escallop
eval() is not a great solution to any problem.Scrouge
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.Ambidexterity
@Scrouge Why is it not?Haddix
@Haddix often but not always "evil" #198269Scrouge
R
135

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]);
Robbery answered 14/12, 2013 at 14:20 Comment(8)
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.Polybasite
what is the purpoes of [0]? can you use nodeScriptReplace(document.getElementById().html);Richardricharda
@BaoThai Yes. You can.Robbery
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().Eonism
@Eonism you sure you have the proper attributes on it ? rel='text/javascritp' for instance?Robbery
I get an erro Uncaught (in promise) TypeError: Cannot read property 'length' of undefined , but I am a beginner. What am I doing wrong?Return
@KatharinaSchreiber there is no promises in this code, so probably not related to this.Robbery
Awesome solution, thank you :) In case anyone is looking for a more modern version of this: https://mcmap.net/q/66898/-can-scripts-be-inserted-with-innerhtmlHive
E
107

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.

Escallop answered 29/7, 2009 at 1:4 Comment(7)
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?Intricate
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.Rockafellow
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.Escallop
eval() is not a great solution to any problem.Scrouge
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.Ambidexterity
@Scrouge Why is it not?Haddix
@Haddix often but not always "evil" #198269Scrouge
S
99

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

Skirling answered 15/9, 2010 at 3:7 Comment(8)
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 :(Ambidexterity
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.Goatsucker
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.Humes
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).Vivisection
You 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 of onload use onerror (but this will do a network request)Egotist
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).Adopt
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!Lowborn
@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)"Menthol
G
54

I do this every time I want to insert a script tag dynamically:

const html =
  `<script>
      alert('👋 there ! Wanna grab a 🍺'); 
  </script>`;

const scriptEl = document.createRange().createContextualFragment(html);
parent.append(scriptEl);

Note: this uses ES6.

I've seen a lot of answers use appendChild and it works exactly as append.

Gametocyte answered 29/6, 2020 at 15:30 Comment(6)
you can use this nice react component - github.com/christo-pr/dangerously-set-html-contentFourlegged
its a relatively new solution to an old problem :PGametocyte
this does not run the script.Veasey
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 ?Alphosis
It does work as intended. I managed to put all in a single line.Henton
Thank you for the answer! Great solution! Small clarification: appendChild and append doesn't work exactly the same: append accepts text/string while appendChild method can only be used to insert Node objects into the DOM. There are certain cases where one or the other is preferred (if you want to enforce using Node objects and avoid string like <p> in your code, or if you want to have strings appended). But in most cases you can use either one.Saffian
C
53

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

Clemmy answered 14/8, 2011 at 0:36 Comment(4)
Unless there aren't any other script elements in the document. Use document.documentElement instead.Mistake
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>Clemmy
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.Mistake
Facebook uses Pablo's answer in their SDK. developers.facebook.com/docs/javascript/quickstart/v2.2#loadingOligoclase
P
31

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
Prowl answered 29/4, 2013 at 12:4 Comment(2)
Thanks. It fixed my problem of adding Disqus Universal code to a modal popup created using TinyBox2 Jquery plugin.Peppy
Unfortunately, this solution does not work when the script contains functions that will be invoked later on.Rafaelof
P
26

I had this problem with innerHTML, I had to append a Hotjar script to the "head" tag of my Reactjs application and it would have to execute right after appending.

One of the good solutions for dynamic Node import into the "head" tag is React-helment module.


Also, there is a useful solution for the proposed issue:

No script tags in innerHTML!

It turns out that HTML5 does not allow script tags to be dynamically added using the innerHTML property. So the following will not execute and there will be no alert saying Hello World!

element.innerHTML = "<script>alert('Hello World!')</script>";

This is documented in the HTML5 spec:

Note: script elements inserted using innerHTML do not execute when they are inserted.

But beware, this doesn't mean innerHTML is safe from cross-site scripting. It is possible to execute JavaScript via innerHTML without using tags as illustrated on MDN's innerHTML page.

Solution: Dynamically adding scripts

To dynamically add a script tag, you need to create a new script element and append it to the target element.

You can do this for external scripts:

var newScript = document.createElement("script");
newScript.src = "http://www.example.com/my-script.js";
target.appendChild(newScript);

And inline scripts:

var newScript = document.createElement("script");
var inlineScript = document.createTextNode("alert('Hello World!');");
newScript.appendChild(inlineScript); 
target.appendChild(newScript);
Primate answered 11/11, 2019 at 16:1 Comment(2)
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.Malorie
@Malorie Sure, There's lots of different ways in programming world to implement stuffs! also that's more clear and maintainable :).Primate
H
12

Here's a more modern (and concise) version of mmm's awesome solution:

function executeScriptElements(containerElement) {
  const scriptElements = containerElement.querySelectorAll("script");

  Array.from(scriptElements).forEach((scriptElement) => {
    const clonedElement = document.createElement("script");

    Array.from(scriptElement.attributes).forEach((attribute) => {
      clonedElement.setAttribute(attribute.name, attribute.value);
    });
    
    clonedElement.text = scriptElement.text;

    scriptElement.parentNode.replaceChild(clonedElement, scriptElement);
  });
}

Note: I've also tried alternative solutions using cloneNode() or outerHTML but that didn't work.

Hive answered 15/9, 2021 at 9:42 Comment(1)
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.Bird
M
8

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;
}
Mudra answered 22/5, 2019 at 2:34 Comment(7)
I mean... Appending a script tag with code content is an eval, is it not?Abrego
@KevinB There are notorious differences ... try eval('console.log(this)') and you will see the most obvious oneMudra
so the context is different, and? it’s still just an eval.Abrego
@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 itMudra
Works for me. CheersAlleenallegation
wow, it does work wellBayonet
this worked really nice, suggestion to add would be to be correctly wait for script onload when src and no async or defer in script attributes. I used Promises + await within loop.Loydloydie
H
7

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);
}
Harts answered 26/6, 2018 at 16:34 Comment(0)
S
5

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.

Scientism answered 4/11, 2016 at 15:21 Comment(2)
I just tried it and it works on chrome 57. innerHTML on a script tag executes the text.Teresaterese
That's interesting, it did not to work before. I wonder if this behavior is cross-browser or only in chrome 57.Scientism
E
5

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.

Eggcup answered 2/5, 2017 at 8:8 Comment(0)
C
5

Filter your script tags and run each of them with eval

var tmp=  document.createElement('div');
tmp.innerHTML = '<script>alert("hello")></script>';
[...tmp.children].filter(x => x.nodeName === 'SCRIPT').forEach(x => eval(x.innerText));
Callboy answered 7/1, 2021 at 22:36 Comment(1)
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));Ellingston
O
3

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;  
}
Ogdoad answered 29/7, 2009 at 12:23 Comment(1)
For what it's worth: this does not appear to work on current browsers.Permutation
E
3

My own twist, using modern JS and typescript. Not sure why people are filtering on tagName etc when querySelector is right there.

Works a charm for me:

function makeScriptsExecutable(el: Element) {
  el.querySelectorAll("script").forEach(script => {
    const clone = document.createElement("script")

    for (const attr of script.attributes) {
      clone.setAttribute(attr.name, attr.value)
    }

    clone.text = script.innerHTML
    script.parentNode?.replaceChild(clone, script)
  })
}
Entebbe answered 23/11, 2021 at 15:36 Comment(0)
P
1

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.
Pumice answered 28/1, 2014 at 16:22 Comment(0)
S
1

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

Sartain answered 7/5, 2014 at 8:27 Comment(1)
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.Dieselelectric
F
1

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>
Fallacious answered 9/2, 2018 at 16:44 Comment(1)
This doesn't work with Microsoft Edge, any other workaround?Outcome
C
1

You can also wrap your <script> like this and it will get executed:

<your target node>.innerHTML = '<iframe srcdoc="<script>alert(top.document.title);</script>"></iframe>';

Please note: The scope inside srcdoc refers to the iframe, so you have to use top like in the example above to access the parent document.

Cyclic answered 6/9, 2019 at 8:28 Comment(0)
I
0

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});
Imminent answered 6/10, 2017 at 13:34 Comment(2)
Thanks for downvoting me without saying why. Love u all.Imminent
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.Gangplank
F
0

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
}
Feudal answered 13/10, 2018 at 20:47 Comment(5)
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.Imminent
@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.Feudal
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.Feudal
u're right... I've missridden it. My apologies aswell.Imminent
@gabrielgarcia Not a problem :)Feudal
I
0

Building up on Danny '365CSI' Engelman's comment, here is an universal solution:

<script>
  alert("This script always runs.");
  script01 = true;
</script>
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"
 onload="if(typeof script01==='undefined') eval(this.previousElementSibling.innerHTML)">

Use this as innerHTML (i.e. loaded by XMLHttpRequest) or directly (i.e. inserted by PHP backend), the script always loads once.

Explanation: script loaded as innerHTML is not executed, but onload content atribute is. If the script was not executed (added as innerHTML) then the script is executed in image onload event. If the script was loaded (added by backend) then script01 variable is defined and onload will not run the script for the second time.

Intermezzo answered 20/8, 2020 at 6:17 Comment(1)
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)"/>Egotist
H
0

Single line solution below:

document.getElementsByTagName("head")[0].append(document.createRange().createContextualFragment('<script src="https://google.com/file.js"></script>'));
Henton answered 20/12, 2022 at 19:16 Comment(1)
so the problem is, when I try to serialize HTML that contains script tags, the "<" is replaced with "\x3C" wrecking my deserializing them by assigning the serialized HTML back to an element by innerHTML.Sweetening
A
-1

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.

Avowal answered 22/1, 2013 at 14:29 Comment(0)
B
-1

For me the best way is to insert the new HTML content through innerHtml and then use

setTimeout(() => {
        var script_el = document.createElement("script")
        script_el.src = 'script-to-add.js'
        document.body.appendChild(script_el)
    }, 500)

The setTimeout is not required but it works better. This worked for me.

Banded answered 25/4, 2020 at 15:43 Comment(0)
S
-1

simple, no eval, no functions:

    fetch('/somepage')
    .then(x=>x.text())
    .then(x=>{
      divDestination.innerHTML=x;
        divDestination.querySelectorAll("script")
        .forEach(x=>{
          var sc=document.createElement("script");
          sc.appendChild(document.createTextNode(x.innerText));
          divDestination.appendChild(sc)
      })      
  })
Sulphuric answered 14/2, 2022 at 8:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.