Insert HTML with Scripts That Should Run

Insert HTML with scripts that should run

You need jQuery (load it before Angular):

"we looked into supporting script tags in jqlite, but what needs to be done to get a cross-browser support involves a lot of black magic. For this reason we decided that for now we are just going to recommend that users use jquery along with angular in this particular case" --
https://groups.google.com/d/msg/angular/H4haaMePJU0/5seG803by5kJ

See also AngularJS: How to make angular load script inside ng-include?

Can scripts be inserted with innerHTML?

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.

Executing <script> elements inserted with .innerHTML

The OP's script doesn't work in IE 7. With help from SO, here's a script that does:

exec_body_scripts: function(body_el) {
// Finds and executes scripts in a newly added element's body.
// Needed since innerHTML does not run scripts.
//
// Argument body_el is an element in the dom.

function nodeName(elem, name) {
return elem.nodeName && elem.nodeName.toUpperCase() ===
name.toUpperCase();
};

function evalScript(elem) {
var data = (elem.text || elem.textContent || elem.innerHTML || "" ),
head = document.getElementsByTagName("head")[0] ||
document.documentElement,
script = document.createElement("script");

script.type = "text/javascript";
try {
// doesn't work on ie...
script.appendChild(document.createTextNode(data));
} catch(e) {
// IE has funky script nodes
script.text = data;
}

head.insertBefore(script, head.firstChild);
head.removeChild(script);
};

// main section of function
var scripts = [],
script,
children_nodes = body_el.childNodes,
child,
i;

for (i = 0; children_nodes[i]; i++) {
child = children_nodes[i];
if (nodeName(child, "script" ) &&
(!child.type || child.type.toLowerCase() === "text/javascript")) {
scripts.push(child);
}
}

for (i = 0; scripts[i]; i++) {
script = scripts[i];
if (script.parentNode) {script.parentNode.removeChild(script);}
evalScript(scripts[i]);
}
};

How to insert and execute script tag to the DOM using plain js

You can use insertAdjacentHTML to insert the HTML, then go back and look for the scripts and copy the script src or text, and insert the copy into the DOM to run it:

// Sticking with broadly-supported features:var htmlString = "<div><h1>Title</h1></div><div><script>console.log('it works');<\/script></div>";var target = document.getElementById("target");target.insertAdjacentHTML("beforeend", htmlString);var scripts = target.getElementsByTagName("script");while (scripts.length) {    var script = scripts[0];    script.parentNode.removeChild(script);    var newScript = document.createElement("script");    if (script.src) {        newScript.src = script.src;    } else if (script.textContent) {        newScript.textContent = script.textContent;    } else if (script.innerText) {        newScript.innerText = script.innerText;    }    document.body.appendChild(newScript);}
<div id="target"></div>

Where should I put <script> tags in HTML markup?

Here's what happens when a browser loads a website with a <script> tag on it:

  1. Fetch the HTML page (e.g. index.html)
  2. Begin parsing the HTML
  3. The parser encounters a <script> tag referencing an external script file.
  4. The browser requests the script file. Meanwhile, the parser blocks and stops parsing the other HTML on your page.
  5. After some time the script is downloaded and subsequently executed.
  6. The parser continues parsing the rest of the HTML document.

Step #4 causes a bad user experience. Your website basically stops loading until you've downloaded all scripts. If there's one thing that users hate it's waiting for a website to load.

Why does this even happen?

Any script can insert its own HTML via document.write() or other DOM manipulations. This implies that the parser has to wait until the script has been downloaded and executed before it can safely parse the rest of the document. After all, the script could have inserted its own HTML in the document.

However, most JavaScript developers no longer manipulate the DOM while the document is loading. Instead, they wait until the document has been loaded before modifying it. For example:

<!-- index.html -->
<html>
<head>
<title>My Page</title>
<script src="my-script.js"></script>
</head>
<body>
<div id="user-greeting">Welcome back, user</div>
</body>
</html>

JavaScript:

// my-script.js
document.addEventListener("DOMContentLoaded", function() {
// this function runs when the DOM is ready, i.e. when the document has been parsed
document.getElementById("user-greeting").textContent = "Welcome back, Bart";
});

Because your browser does not know my-script.js isn't going to modify the document until it has been downloaded and executed, the parser stops parsing.

Antiquated recommendation

The old approach to solving this problem was to put <script> tags at the bottom of your <body>, because this ensures the parser isn't blocked until the very end.

This approach has its own problem: the browser cannot start downloading the scripts until the entire document is parsed. For larger websites with large scripts and stylesheets, being able to download the script as soon as possible is very important for performance. If your website doesn't load within 2 seconds, people will go to another website.

In an optimal solution, the browser would start downloading your scripts as soon as possible, while at the same time parsing the rest of your document.

The modern approach

Today, browsers support the async and defer attributes on scripts. These attributes tell the browser it's safe to continue parsing while the scripts are being downloaded.

async

<script src="path/to/script1.js" async></script>
<script src="path/to/script2.js" async></script>

Scripts with the async attribute are executed asynchronously. This means the script is executed as soon as it's downloaded, without blocking the browser in the meantime.
This implies that it's possible that script 2 is downloaded and executed before script 1.

According to http://caniuse.com/#feat=script-async, 97.78% of all browsers support this.

defer

<script src="path/to/script1.js" defer></script>
<script src="path/to/script2.js" defer></script>

Scripts with the defer attribute are executed in order (i.e. first script 1, then script 2). This also does not block the browser.

Unlike async scripts, defer scripts are only executed after the entire document has been loaded.

(To learn more and see some really helpful visual representations of the differences between async, defer and normal scripts check the first two links at the references section of this answer)

Conclusion

The current state-of-the-art is to put scripts in the <head> tag and use the async or defer attributes. This allows your scripts to be downloaded ASAP without blocking your browser.

The good thing is that your website should still load correctly on the 2% of browsers that do not support these attributes while speeding up the other 98%.

References

  • async vs defer attributes
  • Efficiently load JavaScript with defer and async
  • Remove Render-Blocking JavaScript
  • Async, Defer, Modules: A Visual Cheatsheet

Insert and execute javascript

If you absolutely, positively have to take JavaScript code that's in a string and execute it, you basically have to use eval or an eval-like mechanism. In some years of JavaScript programming, I've never had to resort to it, and I do suggest that you look at whether there's another way to achieve your actual overall goal.

So here, you'd strip off the script tag stuff and just eval the code, e.g.:

var script = foo.replace(/^<script[^>]*>/, "").replace(/<\/script>$/, "");
eval(script);
// Or window.evalInGlobalScope(script); // -- See below

Obviously you have to be sure you trust the source of the string, since you're executing the code therein.

eval is a slippery beast and plays very odd games with context and scope. If you need something that looks more like what you'd get if you did add a script tag to the page, here's a function that does that cross-browser (from my answer to this other question here on Stack Overflow):

window.evalInGlobalScope = (function() {
var fname, scr;

// Get a unique function name
do {
fname = "__eval_in_global_test_" + Math.floor(Math.random() * 100000);
}
while (typeof window[fname] !== 'undefined');

// Create test script
scr = "function " + fname + "() { }";

// Return the first function that works:
return test(evalInGlobalScope_execScript) ||
test(evalInGlobalScope_windowEval) ||
test(evalInGlobalScope_theHardWay) ||
evalInGlobalScope_fail;

function test(f) {
try {
f(scr);
if (typeof window[fname] === 'function') {
return f;
}
}
catch (e) {
return false;
}
finally {
try { delete window[fname]; } catch (e) { window[fname] = undefined; }
}
}
function evalInGlobalScope_execScript(str) {
window.execScript(str);
}
function evalInGlobalScope_windowEval(str) {
window.eval(str);
}
function evalInGlobalScope_theHardWay(str) {
var parent, script, d = document;

parent = d.body || d.documentElement || d.getElementsByTagName('head')[0];
if (parent) {
script = d.createElement('script');
script.appendChild(d.createTextNode(str));
parent.appendChild(script);
}
}
function evalInGlobalScope_fail() {
throw "evalInGlobalScope: Unable to determine how to do global eval in this environment";
}
})();

Live example using the above

setting innerHTML with a script inside

It looks like that your <script> tag is being added as you expect, but the code within it is not being executed. The same failure happens if you try using document.head (or any other DOM element, it seems). For whatever reason (possibly standards compliance, possible security), inline code inside of <script> blocks that are added via .innerHTML simply doesn't run.

However, I do have working code that produces similar functionality:

var script = document.createElement('script');
script[(script.innerText===undefined?"textContent":"innerText")] = 'alert(1);';
document.documentElement.appendChild(script);

Here, you add the <script> block with documentElement.appendChild and use textContent or innerText to set the content of the <script>.

How do I add script dynamically and run it. I am able to add it to my html code, but it is not executed

Ok, I wanted to do something such that I don't have to edit all 40+ articles to add code for an ad.

I could not find a way to insert a script dynamically. Sometimes ad is displayed but out of place (and doesn't look good).

I removed all the code related to ad, and just left with the code blog application without ads.
Then I changed my view, and decided to edit the theme. My site is powered by Ghost, and I am using edition theme.
I downloaded the current theme, and edited it to show an ad at the end of the article.

Now, if I change the ad provider or what so ever, I will have to update the code at only the one place (in the theme).

BTW, my site is https://blog.nirmites.com



Related Topics



Leave a reply



Submit