document.createElement( script ) synchronously
You can create your <script>
element with an "onload" handler, and that will be called when the script has been loaded and evaluated by the browser.
var script = document.createElement('script');
script.onload = function() {
alert("Script loaded and ready");
};
script.src = "http://whatever.com/the/script.js";
document.getElementsByTagName('head')[0].appendChild(script);
You can't do it synchronously.
edit — it's been pointed out that, true to form, IE doesn't fire a "load" event on <script>
tags being loaded/evaluated. Thus I suppose the next thing to do would be to fetch the script with an XMLHttpRequest and then eval()
it yourself. (Or, I suppose, stuff the text into a <script>
tag you add; the execution environment of eval()
is affected by the local scope, so it won't necessarily do what you want it to do.)
edit — As of early 2013, I'd strongly advise looking into a more robust script loading tool like Requirejs. There are a lot of special cases to worry about. For really simple situations, there's yepnope, which is now built into Modernizr.
Dynamically loading JavaScript synchronously
There is only one way to synchronously load and execute a script resource, and that is using a synchronous XHR
This is an example of how to do this
// get some kind of XMLHttpRequest
var xhrObj = createXMLHTTPObject();
// open and send a synchronous request
xhrObj.open('GET', "script.js", false);
xhrObj.send('');
// add the returned content to a newly created script tag
var se = document.createElement('script');
se.type = "text/javascript";
se.text = xhrObj.responseText;
document.getElementsByTagName('head')[0].appendChild(se);
But you shouldn't in general use synchronous requests as this will block everything else.
But that being said, there are of course scenarios where this is appropriate.
I would probably refactor the containing function into an asynchronous pattern though using an onload handler.
document.createElement('script') vs script src=
The <script src=...>
blocks the browser while document.createElement('script')
loads the JavaScript asynchronously; this is the primary reason.
The <script src=...>
tag blocks browser from displaying rest of the page until the script is loaded and executed. This ensures that scripts are executed in correct order and any document.write()
in that script work as expected. However this creates a laggy browsing experience for the user.
When the script is loaded asynchronously, the browser can download the script without blocking the page display. This improves the browsing experience dramatically.
To load the scripts asynchronously one can use HTML markup:
<script src="..." async defer></script>
The async
attribute was introduced in HTML5 while the defer
attribute can be added as a fallback for older versions of IE. This document describes how async and defer attribute work.
Alternately, one can use JavaScript to build a script tag:
var s = document.createElement('script');
s.src = "...";
document.getElementsByTagName("head")[0].appendChild(s);
JavaScript generated script tags work in most browsers even if they do not understand the async
attribute or .async = true
property.
About schemeless URIs (//example.com/script.js
): schemeless URIs seem to work almost everywhere (see this question).
About the Google Analytics example: both old and new code use JavaScript to detect the protocol then load http://www.
or https://ssl.
which is not possible via HTML markup.
Load several scripts in certain order
Yes! There is. Use Promises
and async/await
function loadScript(url) {
return new Promise((resolve, reject) => {
const script = document.createElement('script')
script.src = url
document.head.appendChild(script)
script.onload = () => resolve()
})
}
async function loadScripts() {
await loadScript('script1.js')
await loadScript('script2.js')
// ..
}
loadScripts().then(_ => console.log('All scripts loaded in specified order'))
document.createElement('script')... adding two scripts with one callback
I propose you to use some small loader which will chain and do stuff for you. For example like this one:
function loadScripts(array,callback){
var loader = function(src,handler){
var script = document.createElement("script");
script.src = src;
script.onload = script.onreadystatechange = function(){
script.onreadystatechange = script.onload = null;
handler();
}
var head = document.getElementsByTagName("head")[0];
(head || document.body).appendChild( script );
};
(function run(){
if(array.length!=0){
loader(array.shift(), run);
}else{
callback && callback();
}
})();
}
This script should help you to build the script tags and call your callback when all files are loaded. Invoke is pretty easy:
loadScripts([
"http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js",
"http://ajax.googleapis.com/ajax/libs/prototype/1.6.1.0/prototype.js"
],function(){
alert('All things are loaded');
});
Hope this will help
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]);
}
};
Related Topics
How to Detect Page Navigation on Youtube and Modify Its Appearance Seamlessly
How to Debug JavaScript/Jquery Event Bindings with Firebug or Similar Tools
How to Capture an Arbitrary Number of Groups in JavaScript Regexp
Jquery Click/Toggle Between Two Functions
Concrete JavaScript Regular Expression for Accented Characters (Diacritics)
Callback After All Asynchronous Foreach Callbacks Are Completed
How to Find Prime Numbers Between 0 - 100
Find Out Whether Chrome Console Is Open
Function with Foreach Returns Undefined Even with Return Statement
When Should I Use Jquery's Document.Ready Function
How to Strip HTML Tags from String in JavaScript
What Is the Non-Jquery Equivalent of '$(Document).Ready()'
Open Url in New Window with JavaScript
How to Escape a JSON String Containing Newline Characters Using JavaScript
Operator Precedence with JavaScript Ternary Operator
How to Add New Array Elements at the Beginning of an Array in JavaScript