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:
- Fetch the HTML page (e.g. index.html)
- Begin parsing the HTML
- The parser encounters a
<script>
tag referencing an external script file. - The browser requests the script file. Meanwhile, the parser blocks and stops parsing the other HTML on your page.
- After some time the script is downloaded and subsequently executed.
- 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
Load Iframe Links into Parent Window
Text-Decoration Not Working for Visited State Link
CSS Shape with Inset Curve and Transparent Background
CSS I Want a Div to Be on Top of Everything
Text Wrapping Around an Absolute Positioned Div
How to Remove X and Y on Submit in HTML Form with Image Type Button
Multiple Select in Safari iOS 7
Overflow-Y:Visible Not Working When Overflow-X:Hidden Is Present
Flexbox Layout with Two Equal Height Children, One Containing Nested Flexbox with Scrolling Content
How Much Faster Is It to Use Inline/Base64 Images for a Web Site Than Just Linking to The Hard File
Font Size with Percentage Value (%) Not Scaling with Screen Size
Adding Double Quotes to a Paragraph with CSS
How to Correctly Use "Section" Tag in HTML5
<Input Type="File"> Limit Selectable Files by Extensions
What Are The Allowed Ways to Close Self-Closing Tags for Void Elements Such as <Br> in HTML5