Delay callback until script is added to document? - javascript

How can I get the callback to not run until the script is actually appended to the document?
function addScript(filepath, callback){
if (filepath) {
var fileref = document.createElement('script');
fileref.setAttribute("type","text/javascript");
fileref.setAttribute("src", filepath);
if (typeof fileref!="undefined")
document.getElementsByTagName("head")[0].appendChild(fileref);
}
if (callback) {
callback();
}
}

In the ideal world, you could use the onload property of the <script /> tag;
function addScript(filepath, callback){
if (filepath) {
var fileref = document.createElement('script');
fileref.onload = callback;
fileref.setAttribute("type","text/javascript");
fileref.setAttribute("src", filepath);
if (typeof fileref!="undefined")
document.getElementsByTagName("head")[0].appendChild(fileref);
}
}
However, this doesn't work in IE, so mega-h4x are required;
function addScript(filepath, callback) {
if (filepath) {
var fileref = document.createElement('script');
var done = false;
var head = document.getElementsByTagName("head")[0];
fileref.onload = fileref.onreadystatechange = function () {
if (!done && (!this.readyState || this.readyState === "loaded" || this.readyState === "complete")) {
done = true;
callback();
// Handle memory leak in IE
fileref.onload = fileref.onreadystatechange = null;
if (head && fileref.parentNode) {
head.removeChild(fileref);
}
}
};
fileref.setAttribute("type", "text/javascript");
fileref.setAttribute("src", filepath);
head.appendChild(fileref);
}
}
FWIW, your if (typeof fileref != "undefined") was redundant, as it will always evaluate to true, so you can just do head.appendChild(fileref); directly, as in my example.

what about:
var myParticularCallback = function () {
// do something neat ...
}
..... your code
var fileref = document.createElement('script');
fileref.setAttribute("type","text/javascript");
fileref.onload = myParticularCallback;
if (typeof fileref!="undefined")
document.getElementsByTagName("head")[0].appendChild(fileref);
// after all has set, laod the script and wait for onload event:
fileref.setAttribute("src", filepath);

This tiny script / css loader will do the job
https://github.com/rgrove/lazyload/
LazyLoad.js('/foo.js', function () {
alert('foo.js has been loaded');
});

Easiest way will be define some visible callback() function which will run in your dynamicly included script, at the end.

Related

error loading javascript files dynamically

i am trying to lazy (dynamically) load javascript files in my webpage.
i have used this :
http://www.javascriptkit.com/javatutors/loadjavascriptcss.shtml
function loadjscssfile(filename, filetype){
if (filetype=="js"){ //if filename is a external JavaScript file
var fileref=document.createElement('script')
fileref.setAttribute("type","text/javascript")
fileref.setAttribute("src", filename)
}
else if (filetype=="css"){ //if filename is an external CSS file
var fileref=document.createElement("link")
fileref.setAttribute("rel", "stylesheet")
fileref.setAttribute("type", "text/css")
fileref.setAttribute("href", filename)
}
if (typeof fileref!="undefined")
document.getElementsByTagName("head")[0].appendChild(fileref)
}
loadjscssfile("myscript1.js", "js") //dynamically load and add this .js file
loadjscssfile("myscript2.js", "js") //dynamically load and add this .js file
loadjscssfile("myscript3.js", "js") //dynamically load and add this .js file
these 3 script are to be loaded in a sequence.
myscript3 is dependent on myscript2
and myscript2 is dependent on myscript1.
trying to load it in this way is behaving wierdly.
Looks like these are not loaded in the sequence intended and hence
undefined errors are thrown at times and at times no error are thrown.
Am i doing something wrong.
Updated :
I am using this code to load the files in the correct sequence
function loadjscssfile(filename, filetype) {
if (filetype == "js") { //if filename is a external JavaScript file
var fileref = document.createElement('script')
fileref.setAttribute("type", "text/javascript")
fileref.setAttribute("src", filename)
}
else if (filetype == "css") { //if filename is an external CSS file
var fileref = document.createElement("link")
fileref.setAttribute("rel", "stylesheet")
fileref.setAttribute("type", "text/css")
fileref.setAttribute("href", filename)
}
return fileref;
}
function loadSync(files, types) {
if (typeof (files[0]) === 'undefined') return false;
var script = loadjscssfile(files[0], types[0]);
script.onload = function () {
files.shift();
types.shift();
loadSync(files, types);
}
if (typeof script != "undefined")
document.getElementsByTagName("head")[0].appendChild(script)
}
loadSync(['scripts/module3.base.js', 'scripts/module3.core.js', 'scripts/module3.init.js'], ['js', 'js', 'js']);
console.log('this should get printed when all the three files are loaded');
But the console output is :
this should get printed when all the three files are loaded
scripts/module3.base.js:9 base is initialised
scripts/module3.core.js:6 core is initialised
scripts/module3.init.js:3 app is initialsed
Looks like the first call to loadSync is itself an Asynchronous call
There are open source js which will ease your problem.
You can use LABJS or RequreJS plugins.
Script loaders like LABJS, RequireJS will improve the speed and quality of your code.
You can preserve order of loading your script like this in LABJS.
$LAB.setOptions({AlwaysPreserveOrder:true})
.script("script1.js")
.script("script2.js")
OR
$LAB
.script("script1.js").wait()
.script("script2.js")
script2.js will wait untill script1.js is loaded.
I guess this will solve your problem.
For HTML5 just use async attribute ( info at http://davidwalsh.name/html5-async )
Example of function for loading script with a optional callback option
function loadScript(url, callback) {
if (!callback) callback = function () {};
var script = document.createElement("script")
script.type = "text/javascript";
script.setAttribute("async", "true");
if (script.readyState){ //IE
script.onreadystatechange = function(){
if (script.readyState == "loaded" ||
script.readyState == "complete"){
script.onreadystatechange = null;
callback();
}
};
} else { //Others
script.onload = function(){
callback();
};
}
script.src = url;
document.getElementsByTagName("head")[0].appendChild(script);
}
For a mixed solution:
function loadjscssfile(url, type, callback) {
if (!type) {
type = "js";
}
var node;
if (type === "js") {
var node = document.createElement("script")
node.setAttribute("async", "true");
node.type = "text/javascript";
if (node.readyState){ //IE
node.onreadystatechange = function(){
if (node.readyState == "loaded" ||
node.readyState == "complete"){
node.onreadystatechange = null;
callback();
}
};
} else { //Others
node.onload = function(){
callback();
};
}
node.src = url;
} else if (type === "css") {
var node = document.createElement("link")
node.type = "text/css";
node.setAttribute("rel", "stylesheet");
node.setAttribute("async", "true");
if (node.readyState){ //IE
node.onreadystatechange = function(){
if (node.readyState == "loaded" ||
node.readyState == "complete"){
node.onreadystatechange = null;
callback();
}
};
} else { //Others
node.onload = function(){
callback();
};
}
node.href = url;
}
if (node) {
document.getElementsByTagName("head")[0].appendChild(node);
} else {
callback(new Error("File not found"));
}
}
Loading a list of files
function loadjscssfileList(arr_original, callback) {
var arr = [];
arr_original.forEach(function (v) {
arr.push(v);
});
var next = function () {
if (arr.length) {
var file = arr.shift();
loadjscssfile(file[0], file[1], function () {
next();
});
} else {
callback();
}
};
next();
};
// use without callback
loadjscssfileList([
["myscript.js","js"],
["style.css","css"],
["myscript2.js","js"]
]);
// use with callback
loadjscssfileList([
["myscript.js","js"],
["style.css","css"],
["myscript2.js","js"]
], function () {
alert("loaded")
});
you can load them synchronously.
function get_contents(file){
var xhr = new XMLHttpRequest();
xhr.open('get', file, false);
xhr.send();
return xhr.responseText;
}
function loadjscssfile(filename, filetype){
var content = get_contents(filename);
if (filetype=="js"){ //if filename is a external JavaScript file
var fileref=document.createElement('script');
fileref.setAttribute("type","text/javascript");
fileref.innerHTML = content;
}
else if (filetype=="css"){ //if filename is an external CSS file
var fileref=document.createElement("link")
fileref.setAttribute("rel", "stylesheet")
fileref.setAttribute("type", "text/css")
fileref.innerHTML = content;
}
if (typeof fileref!="undefined")
document.getElementsByTagName("head")[0].appendChild(fileref)
}
To load it externally:
there're 2 ways:
Callback as the another answer.
Or you can do this:
function loadjscssfile(filename, filetype){
if (filetype=="js"){ //if filename is a external JavaScript file
var fileref=document.createElement('script')
fileref.setAttribute("type","text/javascript")
fileref.setAttribute("src", filename)
}
else if (filetype=="css"){ //if filename is an external CSS file
var fileref=document.createElement("link")
fileref.setAttribute("rel", "stylesheet")
fileref.setAttribute("type", "text/css")
fileref.setAttribute("href", filename)
}
return fileref;
}
function loadSync(files,types){
if(files[0] === 'undefined')return false; //if there's no files to load
var script = loadjscssfile(files[0],types[0]); //load first file
script.onload = function(){//after the script is loaded delete them and continue;
files.shift();
types.shift();
loadSync(files,types);
}
if (typeof script !="undefined")
document.getElementsByTagName("head")[0].appendChild(script)
}
loadSync(["myscript1.js","myscript2.js","myscript3.js"],['js','js','js']);

Load jQuery dynamically and use it

How to correctly load jQuery library if it is not loaded yet?
somepage.html:
<script src="http://example.com/js/widget_init.js" type="text/javascript"></script>
<script type="text/javascript">
if (typeof jQuery == 'undefined') {
console.log('ERROR: NOT LOADED');
}
else{
console.log('OK');
}
</script>
The script 'widget_init.js' should load jQuery if it is not loaded yet.
I have this script 'widget_init.js':
function load_script_jquery(){
if (typeof jQuery == 'undefined') {
var jq = document.createElement('script'); jq.type = 'text/javascript';
jq.src = '/path/to/js/jquery.min.js';
document.getElementsByTagName('head')[0].appendChild(jq);
}
else {
}
}
load_script_jquery();
// some other code
The problem is that it doesn't wait for jQuery to be loaded and it shows error like this:
<script type="text/javascript">
if (typeof jQuery == 'undefined') {
console.log('ERROR: NOT LOADED'); // NOT LOADED
}
else{
console.log('OK'); // NEVER GOES HERE
}
</script>
I tried this also without any effect:
document.write('<script src="/path/to/js/jquery.min.js" type="text/javascript"><\/script>');
How to wait until jQuery is loaded so I can use it ?
Your code to append the jQuery script will be appended after your <script> snippet that checks for it. That's how .appendChild works
The Node.appendChild() method adds a node to the end of the list of children of a specified parent node
Source: https://developer.mozilla.org/en-US/docs/Web/API/Node.appendChild (emphasis mine)
Here are two options to solve this:
If you can insert HTML on the page
You can use this snippet from the HTML5 Boilerplate. It will check if another script has already loaded jQuery, and if not, will load it inline.
<script>window.jQuery || document.write('<script src="js/vendor/jquery-1.11.2.min.js"><\/script>')</script>
Just drop it in your head or body tag before the script that depends on it.
If you need to dynamically load it in the Javascript source
Follow the instructions in this tutorial
(function () {
function loadScript(url, callback) {
var script = document.createElement("script")
script.type = "text/javascript";
if (script.readyState) { //IE
script.onreadystatechange = function () {
if (script.readyState == "loaded" || script.readyState == "complete") {
script.onreadystatechange = null;
callback();
}
};
} else { //Others
script.onload = function () {
callback();
};
}
script.src = url;
document.getElementsByTagName("head")[0].appendChild(script);
}
loadScript("https://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js", function () {
//jQuery loaded
console.log('jquery loaded');
});
})();
You just need callback function, after loading jquery secessfully:
var loadJS = function(url, cb) {
var script_tag = document.createElement('script');
script_tag.setAttribute("type", "text/javascript");
script_tag.setAttribute("src",
url);
if (script_tag.readyState) {
script_tag.onreadystatechange = function() { // For old versions of IE
if (this.readyState == 'complete' || this.readyState == 'loaded') {
cb();//calling callback
}
};
} else { // Other browsers
script_tag.onload = cb;//calling callback function
}
(document.getElementsByTagName("head")[0] || document.documentElement).appendChild(script_tag);
};
And simply call this function with jquery library path and callback function reference:
loadJS(hostUrl + "/js/libraries/jquery.js", callback);

What are the difference of adding javascript src vs textual javascript?

What is the difference of adding
<script type="text/javascript" src="helloworld.js">
in document head
vs
adding
<script type="text/javascript">document.write("Hello World"); //content of helloworld.js</script>
I am trying to override Sencha Touch injectScriptElement function by pulling the JS content from Database. Not sure what i am doing wrong:
Ext.apply(Ext.Loader, {
injectScriptElement: function(url, onLoad, onError, scope, charset) {
var script = document.createElement('script'),
me = this,
onLoadFn = function() {
me.cleanupScriptElement(script);
onLoad.call(scope);
},
onErrorFn = function() {
me.cleanupScriptElement(script);
onError.call(scope);
};
script.type = 'text/javascript';
//script.src = url;
script.text = me.getContentFromDatabaseByUrl(url);
script.onload = onLoadFn;
script.onerror = onErrorFn;
script.onreadystatechange = function() {
if (this.readyState === 'loaded' || this.readyState === 'complete') {
onLoadFn();
}
};
if (charset) {
script.charset = charset;
}
me.documentHead.appendChild(script);
return script;
}
});

load external javascript only when needed

I have reference to bunch of javascript file on my page which are used by different functions calls.
When I load the page, first all external JS files get loaded.
Is there anyway so that they get called only when function call occurs for that .js file?
You can add your conditions to this method, which allows you to dynamically add an external javascript file:
function loadScript(url, callback){
var script = document.createElement("script")
script.type = "text/javascript";
if (script.readyState){ //IE
script.onreadystatechange = function(){
if (script.readyState == "loaded" ||
script.readyState == "complete"){
script.onreadystatechange = null;
callback();
}
};
} else { //Others
script.onload = function(){
callback();
};
}
script.src = url;
document.getElementsByTagName("head")[0].appendChild(script);
}
By using JQuery
$.getScript("//platform.twitter.com/widgets.js")
No simple way for get JS fyle for function "on-fly". Also, for load file for function system need some time. May be good solution will using minification for JS-files?
You may try some as this.
loadExternalScriptFile = function(filename, callback) {
var fileref = document.createElement("script");
if (fileref){
fileref.setAttribute("type","text/javascript");
fileref.setAttribute("src", filename);
fileref.onload = callback;
if (typeof fileref != "undefined")
document.getElementsByTagName("head")[0].appendChild(fileref);
}
}
var functionMap = {
'function1': 'js/f1.js',
'function2': 'js/f1.js',
'function3': 'js/f3.js'
};
function checkFunction(functionName, callback) {
if (typeof window[functionName] !== 'function') {
loadExternalScriptFile(functionMap[functionName], callback);
return;
}
callback();
return;
}
//Using
checkFunction('function1', function(){
function1(params);
});
But i think loading all in minification version and single file is better.

Check if script is loaded already. If not, load it asynchronously

I'm trying to load the jQuery JS asynchronously and then i want to call some callback methods when it is completely loaded.
I might be using the same code block over and over again. So i want to check if jQuery(or any other script) is already added before actually adding it.If it is already loaded/loading, then the callback of the current call should be appended to that of the previous call. This is what i'm trying to do.
/*BP Asynchronous JavaScript Loader*/
if (typeof bp_onload_queue == 'undefined') var bp_onload_queue = [];
if (typeof bp_dom_loaded == 'boolean') bp_dom_loaded = false;
else var bp_dom_loaded = false;
if (typeof bp_async_loader != 'function') {
function bp_async_loader(src, callback, id) {
var script = document.createElement('script');
script.type = "text/javascript";
script.async = true;
script.src = src;
script.id = id;
//Check if script previously loaded.
var previous_script = document.getElementById(id);
if (previous_script) if (previous_script.readyState == "loaded" || previous_script.readyState == "complete") {
callback();
alert("had already loaded the same script completely");
return;
} else {
script = previous_script;
}
if (script.onload != null) previous_callback = script.onload;
script.onload = script.onreadystatechange = function() {
var newcallback;
if (previous_script && previous_callback) newcallback = function() {
previous_callback();
callback();
};
else newcallback = callback;
if (bp_dom_loaded) {
newcallback();
} else bp_onload_queue.push(newcallback);
// clean up for IE and Opera
script.onload = null;
script.onreadystatechange = null;
};
var head = document.getElementsByTagName('head')[0];
if (!previous_script) head.appendChild(script);
else alert("had already started loading that script but not complete.so we appended to the previous script's callback");
}
}
if (typeof bp_domLoaded != 'function') function bp_domLoaded(callback) {
bp_dom_loaded = true;
var len = bp_onload_queue.length;
for (var i = 0; i < len; i++) {
bp_onload_queue[i]();
}
}
/*JS gets loaded here */
bp_domLoaded();
/*Loading jQuery Asynchronously */
bp_async_loader("http://code.jquery.com/jquery-1.4.4.js", function() {
alert("script has been loaded : 1");
}, "jQueryjs");
bp_async_loader("http://code.jquery.com/jquery-1.4.4.js", function() {
alert("script has been loaded : 2");
}, "jQueryjs");
Can someone please let me know if there are any errors in this code or if there is a better way of doing this?
I was searching for something similar and found this
http://themergency.com/an-alternative-to-jquerys-getscript-function/

Categories

Resources