getScript creates ReferenceError - javascript

I have some js scripts that I load into my main.js before I start the rest of my code. However during testing it came to my attention that sometimes it creates the following reference error (1 in 8 pageloads or something).
ReferenceError: createContainer is not defined
Now, the only reason that I can think of that I get this error is that when I execute the startScript() function, not all my files are loaded or fully accesable.
Now, perhaps I'm going about this all wrong for including other .js files into my main.js, so I'd like to hear your thoughts about this.
The main.js looks like this:
$(document).ready(function() {
//sets and array of files that should be loaded before anything every happens
var arrFilesToLoad = [ 'scripts/global/variables.js',
'scripts/global/objects.js',
'scripts/global/classes.js'];
var _error;
//walks through the array of items that should be loaded and checks for fails
$.each(arrFilesToLoad , function (key) {
$.getScript(arrFilesToLoad[key])
//when a file is loaded with succes
.done(function () {
//on default send a message to the console
console.log(arrFilesToLoad[key] + 'loaded succesfully');
//if every item is loaded start the script
if(key == (arrFilesToLoad.length - 1)){
startScript();
}
})
//when a file fails to load
.fail(function () {
//add the file that failed to load to a string message
_error += arrFilesToLoad[key] + " - failed to load. \n";
//show an alert with what file failed to load
if(key == (arrFilesToLoad.length - 1)){
alert(_error);
}
});
});
function startScript () {
//set a variable which contains a function that returns a DIV with styling
var oContainer = createContainer();
var oMainMenu = new Menu(arrMainMenu);
$(oContainer).append(createMenu(oMainMenu));
$('body').append(oContainer);
}
});

The issue is because you're loading 3 scripts and presumably only one of them holds the createContainer() function, yet you execute your code when the last request has loaded. This means that you've got a race condition. The last request that was made is not guaranteed to be the last one that is completed. If the other scripts are still being loaded as the final request is completed, you'll see this error.
You can amend your logic so that the callback is only executed once all the scripts have loaded. Try this:
var requests = [];
$.each(arrFilesToLoad , function (key) {
requests.push($.getScript(arrFilesToLoad[key]));
});
$.when.apply(this, requests)
.done(startScript)
.fail(function() {
console.log('one or more scripts failed to load');
});
function startScript() {
var oContainer = createContainer();
var oMainMenu = new Menu(arrMainMenu);
$(oContainer).append(createMenu(oMainMenu));
$('body').append(oContainer);
}

Related

jquery calling null element that actually exists

My code works on localhost, but when I implement it on my site, it doesnt.
The error log says it's calling for an element that doesn't exist. I've reach to the conclusion that it can't see the element because the element is loaded dynamically.
The element is the class .newsitem_text, it's a div that contains a blog post. I believe that the jquery is calling for the class before the class is being loaded by the page.
Here is one example fiddle:
http://jsfiddle.net/ku6L240c
The error:
Uncaught TypeError: Cannot read property 'html' of null
47-ganhe-dinheiro-atraves-de-downloads:1093 Uncaught SyntaxError: Unexpected token ILLEGAL
The code:
<javascript>
var wordList = $(".newsitem_text").html().split(' ');
var newHtml = '';
$.each(wordList, function(index, word){
newHtml += ' ' + word;
if (index == 50) {
newHtml += '<div>Some HTML</div>'
}
})
;
$(".newsitem_text").html(newHtml);
</javascript>
How can I make the script wait until the class is loaded by the page, then it gets executed or something?
Seems you are doing dynamic HTML within JS code and immediately trying to get the just added tags. If that is the case, your code will have to wait and keep checking until browser rendered your new HTML and add the nodes to DOM, then you can query them or do something about.
The only way I found works is usint setTimeOut and keep checking then execute your last statement. I create a function below that checks wait and check for certain condition then execute a call back function.
//A function to wait until either times up, or until a pass in "funct" returns "true", which ever occured first.
//funct - callback function, to be returned true or false.
//done - an optional callback function for notify when waiting is over.
//timeout - the amount of time in million-second to wait.
//caller - optional string of caller for monitoring if multiple waiting session.
function waitToSync(funct, done, timeout, caller) {
//This is a hack synchronize to wait until funct() returns true or timeout becomes < 0.
caller = caller || '';
if ((funct === undefined) || typeof (funct) != 'function') return;
function waiting() {
if (!funct()) {
var dt = new Date();
console.log(caller + " waiting: " + dt.format('yyyy-mm-dd h:MM:ss'));
if ((timeout - 1000) > 0)
setTimeout(waiting, 1000); //1 second.
else {
console.log(caller + ': waitToSync timed out!!!');
document.body.style.cursor = 'default';
}
timeout -= 1000;
}
else {
if (done !== undefined && (typeof done === 'function'))
done();
}
}
waiting();
}
Do all you dynamic or anything to want to wait. Then call the WaitToSync
$.each(wordList, function(index, word){
newHtml += ' ' + word;
if (index == 50) {
newHtml += '<div>Some HTML</div>'
}
});
waitToSync(
function wait() { return document.getElementsByClassName("newsitem_text").length > 0; },
function dosomething() { $(".newsitem_text").html(newHtml); },
10000, //wait up to 10 seconds.
'dynamic add HTML');
You can try to execute this function at window load or at document ready
just put ur function inside this:
$(window).load(function(){
//your function here
});
or here
$(document).ready(function(){
//your function here
});
It is way to better to put the code in the end of the body tag after all your content so javascript can run after everything is loaded !

Javascript, execute scripts code in order inserted into dom tree

NOT A DUPLICATE AS I HAVE YET TO FOUND A SATISFYING ANSWER ON OTHER THREADS:
Load and execute javascript code SYNCHRONOUSLY
Loading HTML and Script order execution
Load and execute javascript code SYNCHRONOUSLY
Looking for native Javascript answers, no jQuery, no requireJS, and so forth please :)
SUMMARY OF THE ENTIRE QUESTION:
I want to asynchronously load scripts but have ordered execution
I am trying to enforce that the code in the inserted script elements execute exactly in the same order as they were added to the dom tree.
That is, if I insert two script tags, first and second, any code in first must fire before the second, no matter who finishes loading first.
I have tried with the async attribute and defer attribute when inserting into the head but doesn't seem to obey.
I have tried with element.setAttribute("defer", "") and element.setAttribute("async", false) and other combinations.
The issue I am experiencing currently has to do when including an external script, but that is also the only test I have performed where there is latency.
The second script, which is a local one is always fired before the first one, even though it is inserted afterwards in the dom tree ( head ).
A) Note that I am still trying to insert both script elements into the DOM. Ofcourse the above could be achieved by inserting first, let it finish and insert the second one, but I was hoping there would be another way because this might be slow.
My understanding is that RequireJS seems to be doing just this, so it should be possible. However, requireJS might be pulling it off by doing it as described in A).
Code if you would like to try directly in firebug, just copy and paste:
function loadScript(path, callback, errorCallback, options) {
var element = document.createElement('script');
element.setAttribute("type", 'text/javascript');
element.setAttribute("src", path);
return loadElement(element, callback, errorCallback, options);
}
function loadElement(element, callback, errorCallback, options) {
element.setAttribute("defer", "");
// element.setAttribute("async", "false");
element.loaded = false;
if (element.readyState){ // IE
element.onreadystatechange = function(){
if (element.readyState == "loaded" || element.readyState == "complete"){
element.onreadystatechange = null;
loadElementOnLoad(element, callback);
}
};
} else { // Others
element.onload = function() {
loadElementOnLoad(element, callback);
};
}
element.onerror = function() {
errorCallback && errorCallback(element);
};
(document.head || document.getElementsByTagName('head')[0] || document.body).appendChild(element);
return element;
}
function loadElementOnLoad(element, callback) {
if (element.loaded != true) {
element.loaded = true;
if ( callback ) callback(element);
}
}
loadScript("http://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js",function() {
alert(1);
})
loadScript("http://ajax.googleapis.com/ajax/libs/chrome-frame/1.0.3/CFInstall.min.js",function() {
alert(2);
})
If you try the above code in like firebug, most often it will fire 2, and then 1. I want to ensure 1 and then 2 but include both in the head.
if I insert two script tags, first and second, any code in first must fire before the second, no matter who finishes loading first. I have tried with the async attribute and defer attribute
No, async and defer won't help you here. Whenever you dynamically insert script elements into the DOM, they are loaded and executed asynchronically. You can't do anything against that.
My understanding is that RequireJS seems to be doing just this
No. Even with RequireJS the scripts are executed asynchronous and not in order. Only the module initialiser functions in those scripts are just define()d, not executed. Requirejs then does look when their dependencies are met and executes them later when the other modules are loaded.
Of course you can reinvent the wheel, but you will have to go with a requirejs-like structure.
Ok, I think I have now came up with a solution.
The trick is that we keep track of each script to be loaded and their order as we insert them into the dom tree. Each of their callback is then registered accordingly to their element.
Then we keep track of when all has finished loading and when they all have, we go through the stack and fire their callbacks.
var stack = [];
stack.loaded = 0;
function loadScriptNew(path, callback) {
var o = { callback: callback };
stack.push(o);
loadScript(path, function() {
o.callbackArgs = arguments;
stack.loaded++;
executeWhenReady();
});
}
function executeWhenReady() {
if ( stack.length == stack.loaded ) {
while(stack.length) {
var o = stack.pop();
o.callback.apply(undefined, o.callbackArgs);
}
stack.loaded = 0;
}
}
// The above is what has been added to the code in the question.
function loadScript(path, callback) {
var element = document.createElement('script');
element.setAttribute("type", 'text/javascript');
element.setAttribute("src", path);
return loadElement(element, callback);
}
function loadElement(element, callback) {
element.setAttribute("defer", "");
// element.setAttribute("async", "false");
element.loaded = false;
if (element.readyState){ // IE
element.onreadystatechange = function(){
if (element.readyState == "loaded" || element.readyState == "complete"){
element.onreadystatechange = null;
loadElementOnLoad(element, callback);
}
};
} else { // Others
element.onload = function() {
loadElementOnLoad(element, callback);
};
}
(document.head || document.getElementsByTagName('head')[0] || document.body).appendChild(element);
return element;
}
function loadElementOnLoad(element, callback) {
if (element.loaded != true) {
element.loaded = true;
if ( callback ) callback(element);
}
}
loadScriptNew("http://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.js",function() {
alert(1);
});
loadScriptNew("http://ajax.googleapis.com/ajax/libs/chrome-frame/1.0.3/CFInstall.min.js",function() {
alert(2);
});
Ok, some of you might argue that there is missing info in the question, which I will give you and here we are actually just solving the callback. You are right. The code in the script is still executed in the wrong order, but the callback is now.
But for me this is good enough, as I intend to wrap all code that is loaded in a method call, alá AMD, such as a require or define call and will put on stack there, and then fire them in the callback instead.
I am still hoping out for Asad and his iframe solution, which I believe might provide the best answer to this question. For me though, this solution will solve my problems :)
I am posting here just like a draft
This do not work because cross-domain police
Here the idea is to obtain all scripts first and when they are in memory, execute them in order.
function loadScript(order, path) {
var xhr = new XMLHttpRequest();
xhr.open("GET",path,true);
xhr.send();
xhr.onreadystatechange = function(){
if(xhr.readyState == 4){
if(xhr.status >= 200 && xhr.status < 300 || xhr == 304){
loadedScripts[order] = xhr.responseText;
}
else {
//deal with error
loadedScripts[order] = 'alert("this is a failure to load script '+order+'");';
// or loadedScripts[order] = ''; // this smoothly fails
}
alert(order+' - '+xhr.status+' > '+xhr.responseText); // this is to show the completion order. Careful, FF stacks aletrs so you see in reverse.
// am I the last one ???
executeAllScripts();
}
};
}
function executeAllScripts(){
if(loadedScripts.length!=scriptsToLoad.length) return;
for(var a=0; a<loadedScripts.length; a++) eval(loadedScripts[a]);
scriptsToLoad = [];
}
var loadedScripts = [];
var scriptsToLoad = [
"http://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js",
"http://ajax.googleapis.com/ajax/libs/chrome-frame/1.0.3/CFInstall.min.js",
"http://nowhere.existing.real_script.com.ar/return404.js"
];
// load all even in reverse order ... or randomly
for(var a=0; a<scriptsToLoad.length; a++) loadScript(a, scriptsToLoad[a]);
After a while of fiddling around with it, here is what I came up with. Requests for the scripts are sent off immediately, but they are executed only in a specified order.
The algorithm:
The algorithm is to maintain a tree (I didn't have time to implement this: right now it is just the degenerate case of a list) of scripts that need to be executed. Requests for all of these are dispatched nearly simultaneously. Every time a script is loaded, two things happen: 1) the script is added to a flat list of loaded scripts, and 2) going down from the root node, as many scripts in each branch that are loaded but have not been executed are executed.
The cool thing about this is that not all scripts need to be loaded in order for execution to begin.
The implementation:
For demonstration purposes, I am iterating backward over the scriptsToExecute array, so that the request for CFInstall is sent off before the request for angularJS. This does not necessarily mean CFInstall will load before angularJS, but there is a better chance of it happening. Regardless of this, angularJS will always be evaluated before CFInstall.
Note that I've used jQuery to make my life easier as far as creating the iframe element and assigning the load handler is concerned, but you can write this without jQuery:
// The array of scripts to load and execute
var scriptsToExecute = [
"http://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js?t=" + Date.now(),
"http://ajax.googleapis.com/ajax/libs/chrome-frame/1.0.3/CFInstall.min.js?t=" + Date.now()
];
// Loaded scripts are stored here
var loadedScripts = {};
// For demonstration purposes, the requests are sent in reverse order.
// They will still be executed in the order specified in the array.
(function start() {
for (var i = scriptsToExecute.length - 1; i >= 0; i--) {
(function () {
var addr = scriptsToExecute[i];
requestData(addr, function () {
console.log("loaded " + addr);
});
})();
}
})();
// This function executes as many scripts as it currently can, by
// inserting script tags with the corresponding src attribute. The
// scripts aren't reloaded, since they are in the cache. You could
// alternatively eval `script.code`
function executeScript(script) {
loadedScripts[script.URL] = script.code
while (loadedScripts.hasOwnProperty(scriptsToExecute[0])) {
var scriptToRun = scriptsToExecute.shift()
var element = document.createElement('script');
element.setAttribute("type", 'text/javascript');
element.setAttribute("src", scriptToRun);
$('head').append(element);
console.log("executed " + scriptToRun);
}
}
// This function fires off a request for a script
function requestData(path, loadCallback) {
var iframe = $("<iframe/>").load(function () {
loadCallback();
executeScript({
URL: $(this).attr("src"),
code: $(this).html()
});
}).attr({"src" : path, "display" : "none"}).appendTo($('body'));
}
You can see a demo here. Observe the console.
cant you nest the loading using ur callbacks?
ie:
loadScript("http://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js",function() {
alert(1);
loadScript("http://ajax.googleapis.com/ajax/libs/chrome-frame/1.0.3/CFInstall.min.js",function() {
alert(2);
})
})

FirefoxOS: return array using device storage api

I am just getting started with coding for FirefoxOS and am trying to get a list of files in a directory.
The idea is to find the name of each file and add it to the array (which works), but I want to return the populated array and this is where I come unstuck. It seems that the array gets populated during the function (as I can get it to spit out file names from it) but when I want to return it to another function it appears to be empty?
Here is the function in question:
function getImageFromDevice (){
var imageHolder = new Array();
var pics = navigator.getDeviceStorage('pictures');
// Let's browse all the images available
var cursor = pics.enumerate();
var imageList = new Array();
var count = 0;
cursor.onsuccess = function () {
var file = this.result;
console.log("File found: " + file.name);
count = count +1;
// Once we found a file we check if there are other results
if (!this.done) {
imageHolder[count] = file.name;
// Then we move to the next result, which call the cursor
// success with the next file as result.
this.continue();
}
console.log("file in array: "+ imageHolder[count]);
// this shows the filename
}
cursor.onerror = function () {
console.warn("No file found: " + this.error);
}
return imageHolder;
}
Thanks for your help!
Enumerating over pictures is an asynchronous call. Essentially what is happening in your code is this:
You are initiating an empty array
You are are telling firefox os to look for pictures on the device
Then in cursor.onsuccess you are telling firefox os to append to the array you have created WHEN it gets back the file. The important thing here is that this does not happen right away, it happens at some point in the future.
Then you are returning the empty array you have created. It's empty because the onsuccess function hasn't actually happened.
After some point in time the onsuccess function will be called. One way to wait until the array is full populated would be to add in a check after:
if (!this.done) {
imageHolder[count] = file.name;
this.continue();
}
else {
//do something with the fully populated array
}
But then of course your code has to go inside the getImageFromDevice function. You can also pass a callback function into the getImageFromDevice function.
See Getting a better understanding of callback functions in JavaScript
The problem is with the aSynchronous nature of the calls you are using.
You are returning (and probably using) the value of imageHolder when it's still empty - as calls to the "onsuccess" function are deferred calls, they happen later in time, whereas your function returns immediately, with the (yet empty) imageHolder value.
You should be doing in this case something along those lines:
function getImageFromDevice (callback){
...
cursor.onsuccess = function () {
...
if (!this.done) {
// next picture
imageHolder[count] = file.name;
this.continue();
} else {
// no more pictures, return with the results
console.log("operation finished:");
callback(imageHolder);
}
}
}
Or use Promises in your code to accomplish the same.
Use the above by e.g.:
getImageFromDevice(function(result) {
console.log(result.length+" pictures found!");
});

Dynamically loading JavaScript synchronously

I'm using the module pattern, one of the things I want to do is dynamically include an external JavaScript file, execute the file, and then use the functions/variables in the file in the return { } of my module.
I can't figure out how to do this easily. Are there any standard ways of performing a pseudo synchronous external script load?
function myModule() {
var tag = document.createElement("script");
tag.type = "text/javascript";
tag.src = "http://some/script.js";
document.getElementsByTagName('head')[0].appendChild(tag);
//something should go here to ensure file is loaded before return is executed
return {
external: externalVariable
}
}
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.
The accepted answer is NOT correct.
Loading a file synchronously is not the same as executing the file synchronously - which is what the OP requested.
The accepted answer loads the file sync, but does nothing more than append a script tag to the DOM. Just because appendChild() has returned does not in anyway guarantee that the script has finished executing and it's members are initialised for use.
The only (see caveat) way to achieve the OPs question is to sync load the script over XHR as stated, then read as text and pass into either eval() or a new Function() call and wait for that function to return. This is the only way to guarantee the script is loaded AND executed synchronously.
I make no comment as to whether this is a wise thing to do either from a UI or security perspective, but there are certainly use cases that justify a sync load & execute.
Caveat:
Unless you're using web workers in which case just call loadScripts();
This is the code that I'm using for multiple file load in my app.
Utilities.require = function (file, callback) {
callback = callback ||
function () {};
var filenode;
var jsfile_extension = /(.js)$/i;
var cssfile_extension = /(.css)$/i;
if (jsfile_extension.test(file)) {
filenode = document.createElement('script');
filenode.src = file;
// IE
filenode.onreadystatechange = function () {
if (filenode.readyState === 'loaded' || filenode.readyState === 'complete') {
filenode.onreadystatechange = null;
callback();
}
};
// others
filenode.onload = function () {
callback();
};
document.head.appendChild(filenode);
} else if (cssfile_extension.test(file)) {
filenode = document.createElement('link');
filenode.rel = 'stylesheet';
filenode.type = 'text/css';
filenode.href = file;
document.head.appendChild(filenode);
callback();
} else {
console.log("Unknown file type to load.")
}
};
Utilities.requireFiles = function () {
var index = 0;
return function (files, callback) {
index += 1;
Utilities.require(files[index - 1], callBackCounter);
function callBackCounter() {
if (index === files.length) {
index = 0;
callback();
} else {
Utilities.requireFiles(files, callback);
}
};
};
}();
And this utilities can be used by
Utilities.requireFiles(["url1", "url2",....], function(){
//Call the init function in the loaded file.
})
The most Node.js-like implementation I could come up with was able to load JS files synchonously, and use them as objects/modules
var scriptCache = [];
var paths = [];
function Import(path)
{
var index = 0;
if((index = paths.indexOf(path)) != -1) //If we already imported this module
{
return scriptCache [index];
}
var request, script, source;
var fullPath = window.location.protocol + '//' + window.location.host + '/' + path;
request = new XMLHttpRequest();
request.open('GET', fullPath, false);
request.send();
source = request.responseText;
var module = (function concealedEval() {
eval(source);
return exports;
})();
scriptCache.push(module);
paths.push(path);
return module;
}
An example source (addobjects.js):
function AddTwoObjects(a, b)
{
return a + b;
}
this.exports = AddTwoObjects;
And use it like this:
var AddTwoObjects = Import('addobjects.js');
alert(AddTwoObjects(3, 4)); //7
//or even like this:
alert(Import('addobjects.js')(3, 4)); //7
the accepted answer is not correct:
the script.async = false; directive only means that html parsing will be paused during script execution. this does not guarantee in which order javascript code will run. see https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/loading-third-party-javascript/
the easiest and most elegant solution which was yet to be mentioned here is using promises, like so:
function loadScript(url) {
return new Promise((resolve, reject) => {
var script = document.createElement('script')
script.src = url
script.onload = () => {
resolve()
}
script.onerror = () => {
reject('cannot load script '+ url)
}
document.body.appendChild(script)
})
}
and then when you want to execute scripts in order:
loadScript('myfirstscript.js').then(() => {
console.log('first script ran');
loadScript('index.js').then(() => {
console.log('second script ran');
})
})
I had the following problem(s) with the existing answers to this question (and variations of this question on other stackoverflow threads):
None of the loaded code was debuggable
Many of the solutions required callbacks to know when loading was finished instead of truly blocking, meaning I would get execution errors from immediately calling loaded (ie loading) code.
Or, slightly more accurately:
None of the loaded code was debuggable (except from the HTML script tag block, if and only if the solution added a script elements to the dom, and never ever as individual viewable scripts.) => Given how many scripts I have to load (and debug), this was unacceptable.
Solutions using 'onreadystatechange' or 'onload' events failed to block, which was a big problem since the code originally loaded dynamic scripts synchronously using 'require([filename, 'dojo/domReady']);' and I was stripping out dojo.
My final solution, which loads the script before returning, AND has all scripts properly accessible in the debugger (for Chrome at least) is as follows:
WARNING: The following code should PROBABLY be used only in 'development' mode. (For 'release' mode I recommend prepackaging and minification WITHOUT dynamic script loading, or at least without eval).
//Code User TODO: you must create and set your own 'noEval' variable
require = function require(inFileName)
{
var aRequest
,aScript
,aScriptSource
;
//setup the full relative filename
inFileName =
window.location.protocol + '//'
+ window.location.host + '/'
+ inFileName;
//synchronously get the code
aRequest = new XMLHttpRequest();
aRequest.open('GET', inFileName, false);
aRequest.send();
//set the returned script text while adding special comment to auto include in debugger source listing:
aScriptSource = aRequest.responseText + '\n////# sourceURL=' + inFileName + '\n';
if(noEval)//<== **TODO: Provide + set condition variable yourself!!!!**
{
//create a dom element to hold the code
aScript = document.createElement('script');
aScript.type = 'text/javascript';
//set the script tag text, including the debugger id at the end!!
aScript.text = aScriptSource;
//append the code to the dom
document.getElementsByTagName('body')[0].appendChild(aScript);
}
else
{
eval(aScriptSource);
}
};
var xhrObj = new XMLHttpRequest();
xhrObj.open('GET', '/filename.js', false);
xhrObj.send(null);
eval(xhrObj.responseText);
If this is a cross-domain request, it will not work. In that case you have to upload the requested file to your server, or make a mirror php that outputs it, and require that php.
With jquery (works with cross-domain request too):
$.getScript('/filename.js',callbackFunction);
callbackFunction will be called synchronously.
For loading more scripts see this thread.
There actually is a way to load a list of scripts and execute them synchronously. You need to insert each script tag into the DOM, explicitly setting its async attribute to false:
script.async = false;
Scripts that have been injected into the DOM are executed asynchronously by default, so you have to set the async attribute to false manually to work around this.
Example
<script>
(function() {
var scriptNames = [
"https://code.jquery.com/jquery.min.js",
"example.js"
];
for (var i = 0; i < scriptNames.length; i++) {
var script = document.createElement('script');
script.src = scriptNames[i];
script.async = false; // This is required for synchronous execution
document.head.appendChild(script);
}
// jquery.min.js and example.js will be run in order and synchronously
})();
</script>
<!-- Gotcha: these two script tags may still be run before `jquery.min.js`
and `example.js` -->
<script src="example2.js"></script>
<script>/* ... */<script>
References
There is a great article by Jake Archibald of Google about this called Deep dive into the murky waters of script loading.
The WHATWG spec on the tag is a good and thorough description of how tags are loaded.
If you need to load an arbitrary number of scripts and only proceed when the last one is done, and you cannot use XHR (e.g. due to CORS limitations) you can do the following. It is not synchronous, but does allow a callback to occur exactly when the last file is done loading:
// Load <script> elements for all uris
// Invoke the whenDone callback function after the last URI has loaded
function loadScripts(uris,whenDone){
if (!uris.length) whenDone && whenDone();
else{
for (var wait=[],i=uris.length;i--;){
var tag = document.createElement('script');
tag.type = 'text/javascript';
tag.src = uris[i];
if (whenDone){
wait.push(tag)
tag.onload = maybeDone;
tag.onreadystatechange = maybeDone; // For IE8-
}
document.body.appendChild(tag);
}
}
function maybeDone(){
if (this.readyState===undefined || this.readyState==='complete'){
// Pull the tags out based on the actual element in case IE ever
// intermingles the onload and onreadystatechange handlers for the same
// script block before notifying for another one.
for (var i=wait.length;i--;) if (wait[i]==this) wait.splice(i,1);
if (!wait.length) whenDone();
}
}
}
Edit: Updated to work with IE7, IE8, and IE9 (in quirks mode). These IE versions do not fire an onload event, but do for onreadystatechange. IE9 in standards mode fires both (with onreadystatechange for all scripts firing before onload for any).
Based on this page there may be a small chance that old versions of IE will never send an onreadystatechange event with readyState=='complete'; if this is the case (I could not reproduce this problem) then the above script will fail and your callback will never be invoked.
You can't and shouldn't perform server operations synchronously for obvious reasons. What you can do, though, is to have an event handler telling you when the script is loaded:
tag.onreadystatechange = function() { if (this.readyState == 'complete' || this.readyState == 'loaded') this.onload({ target: this }); };
tag.onload = function(load) {/*init code here*/}
onreadystatechange delegation is, from memory, a workaround for IE, which has patchy support for onload.
same as Sean's answer, but instead of creating a script tag, just evaluate it. this ensures that the code is actually ready to use.
My strategy, classic example when load jQuery UI, i hope this can help you
( function( tools, libs ){
// Iterator
var require = function( scripts, onEnd ){
onEnd = onEnd || function(){};
if( !scripts || scripts.length < 1 )return onEnd();
var src = scripts.splice( 0, 1),
script = document.createElement( "script" );
script.setAttribute( "src", src );
tools.addEvent( "load", script, function(){
require( scripts, onEnd );
} );
document.getElementsByTagName( "head" )[ 0 ].appendChild( script );
};
// Install all scripts with a copy of scripts
require( libs.slice(), function(){
alert( "Enjoy :)" );
} );
// Timeout information
var ti = setTimeout( function(){
if( !window.jQuery || !window.jQuery.ui )alert( "Timeout !" );
clearTimeout( ti );
}, 5000 );
} )(
{ // Tools
addEvent : function( evnt, elem, func ){
try{
if( elem.addEventListener ){
elem.addEventListener( evnt, func, false );
}else if( elem.attachEvent ){
var r = elem.attachEvent( "on" + evnt, func );
}
return true;
}catch( e ){
return false;
}
}
},
[ // Scripts
"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.0.0-alpha1/jquery.min.js",
"https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.11.4/jquery-ui.min.js"
]
);
When using Angular you can take advantage of the fact that every Provider is instantiated before other services are instantiated. You can combine this fact with using xhr and the eval() as mentioned by #Neil. The code would be following:
app.provider('SomeScriptSyncLoader', function() {
var resourceUrl = 'http://some/script.js';
var dummy = {};
this.$get = function() {
var q = jQuery.ajax({
type: 'GET', url: resourceUrl, cache: false, async: false
});
if (q.status === 200) {
eval(q.responseText); // execute some script synchronously as inline script - eval forces sync processing
}
return dummy;
};
});
To force the Provider to be inialized you need to inject it in at least one other directive/service. Preferably this would be the service which takes advantage of the code loaded by script.
app.directive('myDirective', ['SomeScriptSyncLoader', function(someScriptSyncLoader) {
return {
restrict: 'E',
link: function(scope, element, attrs) {
// some ode
},
template: "this is my template"
};
}]);
I know this is an old question, but maybe someone else read this and find it useful !
Just created a new components uses ES6 to load scripts dynamically in synchronous way.
The Project details and source code are on GitHub https://github.com/amgadfahmi/scripty
I may be late to answering this question.
My current solution is to recursively add <script> tags such that the addition of the subsequent script is in the callback of its predecessor. It assumes that each function contains one function and that function is the same as the file name (minus the extension). This probably isn't the best way to do things, but it works ok.
Code to consider
Code directory structure:
- directory
---- index.html
---- bundle.js
---- test_module/
-------- a.js
-------- b.js
-------- log_num.js
-------- many_parameters.js
index.html
<head>
<script src="bundle.js"></script>
</head>
bundle.js
// Give JS arrays the .empty() function prototype
if (!Array.prototype.empty){
Array.prototype.empty = function(){
return this.length == 0;
};
};
function bundle(module_object, list_of_files, directory="") {
if (!list_of_files.empty()) {
var current_file = list_of_files.pop()
var [function_name, extension] = current_file.split(".")
var new_script = document.createElement("script")
document.head.appendChild(new_script)
new_script.src = directory + current_file
new_script.onload = function() {
module_object[function_name] = eval(function_name)
bundle(module_object, list_of_files, directory)
/*
nullify the function in the global namespace as - assumed - last
reference to this function garbage collection will remove it. Thus modules
assembled by this function - bundle(obj, files, dir) - must be called
FIRST, else one risks overwritting a funciton in the global namespace and
then deleting it
*/
eval(function_name + "= undefined")
}
}
}
var test_module = {}
bundle(test_module, ["a.js", "b.js", "log_num.js", "many_parameters.js"], "test_module/")
a.js
function a() {
console.log("a")
}
b.js
function b() {
console.log("b")
}
log_num.js
// it works with parameters too
function log_num(num) {
console.log(num)
}
many_parameters.js
function many_parameters(a, b, c) {
var calc = a - b * c
console.log(calc)
}
here is my code
var loaded_script = [];
function loadScript(urls, callback, sync) {
var len = urls.length, count = 0;
// check are all js loaded, then execute callback (if any)
var check = function() {
if (count == len) {
callback && typeof callback=="function" && callback();
}
};
for (var i = 0; i < len; i++) {
var url = urls[i];
// check if script not loaded (prevent load again)
if (loaded_script.indexOf(url) == -1) {
var script = document.createElement("script");
script.type = "text/javascript";
// set sync loading here (default is async)
if (sync) {
script.async = false;
}
// script onload event
if (script.readyState) { // IE
script.onreadystatechange = function() {
if (script.readyState=="loaded" || script.readyState=="complete") {
script.onreadystatechange = null;
count++, check();
}
};
} else { // Others
script.onload = function() {
count++, check();
};
}
// add script to head tag
script.src = url;
document.getElementsByTagName("head")[0].appendChild(script);
// mark this script has loaded
loaded_script.push(url);
} else {
count++, check();
}
}
}
I use this on pjax site.
loadScript(
[
"js/first.js",
"js/second.js",
],
function() {
alert("Scripts loaded.");
},
true
);
I've had a similar task a few days earlier, and here's how I did it.
This loader works both in file:// prefixes as well as in http:// and https://, and is cross-browser compatible.
It however, cannot load specific classes or functions as modules from scripts; it will load the whole script altogether and make it available to the DOM.
// Loads a script or an array of scripts (including stylesheets)
// in their respective index order, synchronously.
// By Sayanjyoti Das #https://stackoverflow.com/users/7189950/sayanjyoti-das
var Loader={
queue: [], // Scripts queued to be loaded synchronously
loadJsCss: function(src, onl) {
var ext=src.toLowerCase().substring(src.length-3, src.length);
if(ext=='.js') {
var scrNode=el('script', null, null, null);
scrNode.type='text/javascript';
scrNode.onload=function() {onl();};
scrNode.src=src;
document.body.appendChild(scrNode);
}else if(ext=='css') {
var cssNode=el('link', null, null, null);
cssNode.rel='stylesheet';
cssNode.type='text/css';
cssNode.href=src;
document.head.appendChild(cssNode);
onl();
}
},
add: function(data) {
var ltype=(typeof data.src).toLowerCase();
// Load a single script
if(ltype=='string') {
data.src=data.src;
Loader.queue.splice(0, 1, data, Loader.queue[0]);
Loader.next();
}
// Load an array of scripts
else if(ltype=='object') {
for(var i=data.src.length-1; i>=0; i--) {
Loader.queue.splice(0, 1, {
src: data.src[i],
onload: function() {
if(Loader.next()==false) {
data.onload();
return;
}
Loader.next();
}
}, Loader.queue[0]);
}
Loader.next();
}
},
next: function() {
if(Loader.queue.length!=0 && Loader.queue[0]) {
var scr=Loader.queue[0];
// Remove the script from the queue
if(Loader.queue.length>1)
Loader.queue.splice(0, 2, Loader.queue[1]);
else
Loader.queue=[];
// Load the script
Loader.loadJsCss(scr.src, scr.onload);
}else return false;
}
};
The above function is very powerful and elegant; it allows you to load a single script or an array of script synchronously (i.e, next script not loaded until previous script loading finished). Moreover, a loaded script may load more scripts, which defers the queue in the parent script.
BTW, a script here means a JavaScript file or a CSS stylesheet.
Here's how to use it:-
// Load a single script
Loader.add({
src: 'test.js',
onload: function() {
alert('yay!');
}
});
// Load multiple scripts
Loader.add({
src: ['test1.js', 'test2.js', 'mystyles.css', 'test3.js'],
onload: function() {
alert('all loaded!');
}
});
Note that, the onload function in the Loader arguments is called when all of the scripts have loaded, not when one or a single script is loaded.
You can also load more scripts in the scripts you loaded, such as in test.js, test1.js, etc. By doing this, you will defer the load of the next parent script and the queue in the child script will be prioritized.
Hope it helps :-)
I use jquery load method applied to div element. something like
<div id="js">
<!-- script will be inserted here -->
</div>
...
$("#js").load("path", function() { alert("callback!" });
You can load scripts several times and each time one script will completely replace the one loaded earlier

Get the url of currently executing js file when dynamically loaded

So I'm trying to load a script dynamically and figure out the URL path at which that script was loaded. So some guy gave me a pretty awesome solution to this problem if the scripts are statically loaded ( How to get the file-path of the currently executing javascript code ). But I need a dynamically loaded solution. For example:
$(function()
{ $.getScript("brilliant.js", function(data, textStatus)
{ // do nothing
});
});
where "brilliant.js" has:
var scripts = document.getElementsByTagName("script");
var src = scripts[scripts.length-1].src;
alert("THIS IS: "+src);
Ideally this should either print out "brilliant.js" or "〈hostname+basepath〉/brilliant.js"
Currently brilliant.js works for statically included scripts, but not for scripts included dynamically (like with $.getScript). Anyone have any ideas? Is there somewhere in the dom that stores all the scripts that have been loaded?
EDIT: Andras gave a pretty good solution, though it probably only works for jQuery. Since that's probably the most popular library, and definitely what I'm going to be using. It can probably be extended for other libraries as well. Here's my simplified version:
var scriptUri;
curScriptUrl(function(x)
{ scriptUri = x;
alert(scriptUri);
});
function curScriptUrl(callback)
{ var scripts = document.getElementsByTagName("script");
var scriptURI = scripts[scripts.length-1].src;
if(scriptURI != "") // static include
{ callback(scriptURI);
}else if($ != undefined) // jQuery ajax
{ $(document).ajaxSuccess(function(e, xhr, s)
{ callback(s.url);
});
}
}
When your script gets loaded with jQuery (and I guess other frameworks as well), your script will become indistinguishable from a script that was originally in the HTML document. jQuery makes a request reaching out for your script and puts back the reply as the text child of a <script> node. Your browser has no way of knowing where it originated from, whether it was modified before inserted, etc. It is just a script node as far as she is concerned.
There can be workarounds, however. In the case of jQuery, you can hook up to the ajax events and exploit the fact that they are called right after your script executes. Basically, this would yield "brilliant.js" in your example:
var handler = function (e, xhr, s) {
alert(s.url);
}
$(document).ajaxSuccess(handler);
A more elaborate one:
(function ($, undefined) {
/* Let's try to figure out if we are inlined.*/
var scripts = document.getElementsByTagName("script");
if (scripts[scripts.length - 1].src.length === 0) {
// Yes, we are inlined.
// See if we have jQuery loading us with AJAX here.
if ($ !== undefined) {
var initialized = false;
var ajaxHandler = function (e, xhr, s) {
if (!initialized) {
initialized = true;
alert("Inlined:" + s.url);
initmywholejsframework();
}
}
//If it is, our handler will be called right after this file gets loaded.
$(document).ajaxSuccess(ajaxHandler);
//Make sure to remove our handler if we ever yield back.
window.setTimeout(function () {
jQuery(document).unbind("ajaxSuccess", ajaxHandler);
if (!initialized) {
handleInlinedNonjQuery();
}
}, 0);
}
} else {
//We are included.
alert("Included:" + scripts[scripts.length - 1].src);
initmywholejsframework();
}
//Handle other JS frameworks etc. here, if you will.
function handleInlinedNonjQuery() {
alert("nonJQuery");
initmywholejsframework();
}
//Initialize your lib here
function initmywholejsframework() {
alert("loaded");
}
})(jQuery);
B T, sorry if this doesn't help, but I'm curious why you would need to do this? The reason I'm asking is I don't see why you can't just use the relative file paths to load these files? Finding out where you're located could be done with window.location, but why would you? And as for loading them, can't you make an ajax call to the file and then eval them?
This will work in every browser except IE and doesn't depend on assuming what the name of a file is:
var getErrorLocation = function (error) {
var loc, replacer = function (stack, matchedLoc) {
loc = matchedLoc;
};
if ("fileName" in error) {
loc = error.fileName;
} else if ("stacktrace" in error) { // Opera
error.stacktrace.replace(/Line \d+ of .+ script (.*)/gm, replacer);
} else if ("stack" in error) { // WebKit
error.stack.replace(/at (.*)/gm, replacer);
loc = loc.replace(/:\d+:\d+$/, ""); // remove line number
}
return loc;
};
try {
0();
} catch (e) {
var scriptURI = getErrorLocation(e);
}
alert("THIS IS: " + scriptURI);

Categories