How to include multiple js files using jQuery $.getScript() method
JavascriptJqueryHtmlPromiseJavascript Problem Overview
I am trying to dynamically include javascript files into my js file. I did some research about it and find jQuery $.getScript() method would be a desired way to go.
// jQuery
$.getScript('/path/to/imported/script.js', function()
{
// script is now loaded and executed.
// put your dependent JS here.
// what if the JS code is dependent on multiple JS files?
});
But I am wondering whether this method can load multiple scripts at one time? Why I am asking this is because sometimes my javascript file is depending on more than one js files.
Thank you in advance.
Javascript Solutions
Solution 1 - Javascript
The answer is
You can use promises with getScript()
and wait until all the scripts are loaded, something like:
$.when(
$.getScript( "/mypath/myscript1.js" ),
$.getScript( "/mypath/myscript2.js" ),
$.getScript( "/mypath/myscript3.js" ),
$.Deferred(function( deferred ){
$( deferred.resolve );
})
).done(function(){
//place your code here, the scripts are all loaded
});
In the above code, adding a Deferred and resolving it inside $()
is like placing any other function inside a jQuery call, like $(func)
, it's the same as
$(function() { func(); });
i.e. it waits for the DOM to be ready, so in the above example $.when
waits for all the scripts to be loaded and for the DOM to be ready because of the $.Deferred
call which resolves in the DOM ready callback.
For more generic use, a handy function
A utility function that accepts any array of scripts could be created like this :
$.getMultiScripts = function(arr, path) {
var _arr = $.map(arr, function(scr) {
return $.getScript( (path||"") + scr );
});
_arr.push($.Deferred(function( deferred ){
$( deferred.resolve );
}));
return $.when.apply($, _arr);
}
which can be used like this
var script_arr = [
'myscript1.js',
'myscript2.js',
'myscript3.js'
];
$.getMultiScripts(script_arr, '/mypath/').done(function() {
// all scripts loaded
});
where the path will be prepended to all scripts, and is also optional, meaning that if the array contain the full URL's one could also do this, and leave out the path all together
$.getMultiScripts(script_arr).done(function() { ...
Arguments, errors etc.
As an aside, note that the done
callback will contain a number of arguments matching the passed in scripts, each argument representing an array containing the response
$.getMultiScripts(script_arr).done(function(response1, response2, response3) { ...
where each array will contain something like [content_of_file_loaded, status, xhr_object]
.
We generally don't need to access those arguments as the scripts will be loaded automatically anyway, and most of the time the done
callback is all we're really after to know that all scripts have been loaded, I'm just adding it for completeness, and for the rare occasions when the actual text from the loaded file needs to be accessed, or when one needs access to each XHR object or something similar.
Also, if any of the scripts fail to load, the fail handler will be called, and subsequent scripts will not be loaded
$.getMultiScripts(script_arr).done(function() {
// all done
}).fail(function(error) {
// one or more scripts failed to load
}).always(function() {
// always called, both on success and error
});
Solution 2 - Javascript
I implemented a simple function to load multiple scripts in parallel:
Function
function getScripts(scripts, callback) {
var progress = 0;
scripts.forEach(function(script) {
$.getScript(script, function () {
if (++progress == scripts.length) callback();
});
});
}
Usage
getScripts(["script1.js", "script2.js"], function () {
// do something...
});
Solution 3 - Javascript
Load the following up needed script in the callback of the previous one like:
$.getScript('scripta.js', function()
{
$.getScript('scriptb.js', function()
{
// run script that depends on scripta.js and scriptb.js
});
});
Solution 4 - Javascript
Sometimes it is necessary to load scripts in a specific order. For example jQuery must be loaded before jQuery UI. Most examples on this page load scripts in parallel (asynchronously) which means order of execution is not guaranteed. Without ordering, script y
that depends on x
could break if both are successfully loaded but in wrong order.
I propose a hybrid approach which allows sequential loading of dependent scripts + optional parallel loading + deferred objects:
/*
* loads scripts one-by-one using recursion
* returns jQuery.Deferred
*/
function loadScripts(scripts) {
var deferred = jQuery.Deferred();
function loadScript(i) {
if (i < scripts.length) {
jQuery.ajax({
url: scripts[i],
dataType: "script",
cache: true,
success: function() {
loadScript(i + 1);
}
});
} else {
deferred.resolve();
}
}
loadScript(0);
return deferred;
}
/*
* example using serial and parallel download together
*/
// queue #1 - jquery ui and jquery ui i18n files
var d1 = loadScripts([
"https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.1/jquery-ui.min.js",
"https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.1/i18n/jquery-ui-i18n.min.js"
]).done(function() {
jQuery("#datepicker1").datepicker(jQuery.datepicker.regional.fr);
});
// queue #2 - jquery cycle2 plugin and tile effect plugin
var d2 = loadScripts([
"https://cdn.rawgit.com/malsup/cycle2/2.1.6/build/jquery.cycle2.min.js",
"https://cdn.rawgit.com/malsup/cycle2/2.1.6/build/plugin/jquery.cycle2.tile.min.js"
]).done(function() {
jQuery("#slideshow1").cycle({
fx: "tileBlind",
log: false
});
});
// trigger a callback when all queues are complete
jQuery.when(d1, d2).done(function() {
console.log("All scripts loaded");
});
@import url("https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/themes/blitzer/jquery-ui.min.css");
#slideshow1 {
position: relative;
z-index: 1;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<p><input id="datepicker1"></p>
<div id="slideshow1">
<img src="https://dummyimage.com/300x100/FC0/000">
<img src="https://dummyimage.com/300x100/0CF/000">
<img src="https://dummyimage.com/300x100/CF0/000">
</div>
The scripts in both queues will download in parallel, however, the scripts in each queue will download in sequence, ensuring ordered execution. Waterfall chart:
Solution 5 - Javascript
Use yepnope.js or Modernizr (which includes yepnope.js as Modernizr.load
).
UPDATE
Just to follow up, here's a good equivalent of what you currently have using yepnope, showing dependencies on multiple scripts:
yepnope({
load: ['script1.js', 'script2.js', 'script3.js'],
complete: function () {
// all the scripts have loaded, do whatever you want here
}
});
Solution 6 - Javascript
I ran into a number of issues with multi script loading inculding one issue with (at least in Chrome) same domain hot loading of scripts not actually running after being successfully loaded by Ajax where as Cross Domain works perfectly fine! :(
The selected answer to original question does not work reliably.
After many many iterations here is my final answer to getScript(s) and loading asynchronously multiple scripts in a specific strict order with per script loaded callback option and overall callback on completion, Tested in jQuery 2.1+ and modern versions of Chrome, Firefox plus the forsaken Internet Explorer.
My test case was loading files for a THREE.JS webGL render then starting the render script when THREE global became available using an interval check passed to an anonymous function call to onComplete.
The Prototype function ( getScripts )
function getScripts( scripts, onScript, onComplete )
{
this.async = true;
this.cache = false;
this.data = null;
this.complete = function () { $.scriptHandler.loaded(); };
this.scripts = scripts;
this.onScript = onScript;
this.onComplete = onComplete;
this.total = scripts.length;
this.progress = 0;
};
getScripts.prototype.fetch = function() {
$.scriptHandler = this;
var src = this.scripts[ this.progress ];
console.log('%cFetching %s','color:#ffbc2e;', src);
$.ajax({
crossDomain:true,
async:this.async,
cache:this.cache,
type:'GET',
url: src,
data:this.data,
statusCode: {
200: this.complete
},
dataType:'script'
});
};
getScripts.prototype.loaded = function () {
this.progress++;
if( this.progress >= this.total ) {
if(this.onComplete) this.onComplete();
} else {
this.fetch();
};
if(this.onScript) this.onScript();
};
How to use
var scripts = new getScripts(
['script1.js','script2.js','script.js'],
function() {
/* Optional - Executed each time a script has loaded (Use for Progress updates?) */
},
function () {
/* Optional - Executed when the entire list of scripts has been loaded */
}
);
scripts.fetch();
The function is as it is for I found using Deferred ( Deprecated now? ), When, Success & Complete in my trials to NOT be 100% reliable!?, Hence this function and use of statusCode for example.
You may want to add in error/fail handling behaviour if you wish.
Solution 7 - Javascript
You could make use of the $.when
-method by trying the following function:
function loadScripts(scripts) {
scripts.forEach(function (item, i) {
item = $.getScript(item);
});
return $.when.apply($, scripts);
}
This function would be used like this:
loadScripts(['path/to/script-a.js', 'path/to/script-b.js']).done(function (respA, respB) {
// both scripts are loaded; do something funny
});
That's the way to use Promises and have a minimum of overhead.
Solution 8 - Javascript
Great answer, adeneo.
It took me a little while to figure out how to make your answer more generic (so that I could load an array of code-defined scripts). Callback gets called when all scripts have loaded and executed. Here is my solution:
function loadMultipleScripts(scripts, callback){
var array = [];
scripts.forEach(function(script){
array.push($.getScript( script ))
});
array.push($.Deferred(function( deferred ){
$( deferred.resolve );
}));
$.when.apply($, array).done(function(){
if (callback){
callback();
}
});
}
Solution 9 - Javascript
Append scripts with async=false
Here's a different, but super simple approach. To load multiple scripts you can simply append them to body.
- Loads them asynchronously, because that's how browsers optimize the page loading
- Executes scripts in order, because that's how browsers parse the HTML tags
- No need for callback, because scripts are executed in order. Simply add another script, and it will be executed after the other scripts
https://www.html5rocks.com/en/tutorials/speed/script-loading/
More info here:var scriptsToLoad = [
"script1.js",
"script2.js",
"script3.js",
];
scriptsToLoad.forEach(function(src) {
var script = document.createElement('script');
script.src = src;
script.async = false;
document.body.appendChild(script);
});
Solution 10 - Javascript
Here is answer using Maciej Sawicki's one and implementing Promise
as callback:
function loadScripts(urls, path) {
return new Promise(function(resolve) {
urls.forEach(function(src, i) {
let script = document.createElement('script');
script.type = 'text/javascript';
script.src = (path || "") + src;
script.async = false;
// If last script, bind the callback event to resolve
if(i == urls.length-1) {
// Multiple binding for browser compatibility
script.onreadystatechange = resolve;
script.onload = resolve;
}
// Fire the loading
document.body.appendChild(script);
});
});
}
Use:
let JSDependencies = ["jquery.js",
"LibraryNeedingJquery.js",
"ParametersNeedingLibrary.js"];
loadScripts(JSDependencies,'JavaScript/').then(taskNeedingParameters);
All Javascript files are downloaded as soon as possible and executed in the given order. Then taskNeedingParameters
is called.
Solution 11 - Javascript
What you are looking for is an AMD compliant loader (like require.js).
http://requirejs.org/docs/whyamd.html
There are many good open source ones if you look it up. Basically this allows you to define a module of code, and if it is dependent on other modules of code, it will wait until those modules have finished downloading before proceeding to run. This way you can load 10 modules asynchronously and there should be no problems even if one depends on a few of the others to run.
Solution 12 - Javascript
This function will make sure that a file is loaded after the dependency file is loaded completely. You just need to provide the files in a sequence keeping in mind the dependencies on other files.
function loadFiles(files, fn) {
if (!files.length) {
files = [];
}
var head = document.head || document.getElementsByTagName('head')[0];
function loadFile(index) {
if (files.length > index) {
var fileref = document.createElement('script');
fileref.setAttribute("type", "text/javascript");
fileref.setAttribute("src", files[index]);
head.appendChild(fileref);
index = index + 1;
// Used to call a callback function
fileref.onload = function () {
loadFile(index);
}
} else if(fn){
fn();
}
}
loadFile(0);
}
Solution 13 - Javascript
This works for me:
function getScripts(scripts) {
var prArr = [];
scripts.forEach(function(script) {
(function(script){
prArr .push(new Promise(function(resolve){
$.getScript(script, function () {
resolve();
});
}));
})(script);
});
return Promise.all(prArr, function(){
return true;
});
}
And use it:
var jsarr = ['script1.js','script2.js'];
getScripts(jsarr).then(function(){
...
});
Solution 14 - Javascript
Shorter version of Andrew Marc Newton's comprehensive answer above. This one does not check the status code for success, which you should do to avoid undefined UI behaviour.
This one was for an annoying system where I could guarantee jQuery but no other includes, so I wanted a technique short enough to not be farmed off into an external script if forced into it. (You could make it even shorter by passing the index 0 to the first "recursive" call but force of style habits made me add the sugar).
I'm also assigning the dependency list to a module name, so this block can be included anywhere you need "module1" and the scripts and dependent initialization will only be included/run once (you can log index
in the callback and see a single ordered set of AJAX requests runnning)
if(typeof(__loaders) == 'undefined') __loaders = {};
if(typeof(__loaders.module1) == 'undefined')
{
__loaders.module1 = false;
var dependencies = [];
dependencies.push('/scripts/loadmefirst.js');
dependencies.push('/scripts/loadmenext.js');
dependencies.push('/scripts/loadmelast.js');
var getScriptChain = function(chain, index)
{
if(typeof(index) == 'undefined')
index = 0;
$.getScript(chain[index],
function()
{
if(index == chain.length - 1)
{
__loaders.module1 = true;
/* !!!
Do your initialization of dependent stuff here
!!! */
}
else
getScriptChain(chain, index + 1);
}
);
};
getScriptChain(dependencies);
}
Solution 15 - Javascript
There's a plugin out there that extends jQuery's getScript method. Allows for asynchronous and synchronous loading and uses jQuery's caching mechanism. Full disclosure, I wrote this. Please feel free to contribute if you find a better method.
Solution 16 - Javascript
Loads n scripts one by one (useful if for example 2nd file needs the 1st one):
(function self(a,cb,i){
i = i || 0;
cb = cb || function(){};
if(i==a.length)return cb();
$.getScript(a[i++],self.bind(0,a,cb,i));
})(['list','of','script','urls'],function(){console.log('done')});
Solution 17 - Javascript
based on answer from @adeneo above: Combining both loading of css and js files
any suggestions for improvements ??
// Usage
//$.getMultiResources(['script-1.js','style-1.css'], 'assets/somePath/')
// .done(function () {})
// .fail(function (error) {})
// .always(function () {});
(function ($) {
$.getMultiResources = function (arr, pathOptional, cache) {
cache = (typeof cache === 'undefined') ? true : cache;
var _arr = $.map(arr, function (src) {
var srcpath = (pathOptional || '') + src;
if (/.css$/i.test(srcpath)) {
return $.ajax({
type: 'GET',
url: srcpath,
dataType: 'text',
cache: cache,
success: function () {
$('<link>', {
rel: 'stylesheet',
type: 'text/css',
'href': srcpath
}).appendTo('head');
}
});
} else {
return $.ajax({
type: 'GET',
url: srcpath,
dataType: 'script',
cache: cache
});
}
});
//
_arr.push($.Deferred(function (deferred) {
$(deferred.resolve);
}));
//
return $.when.apply($, _arr);
};
})(jQuery);
Solution 18 - Javascript
You can give this a try using recursion. This will download them in sync, one after another until it completes downloading the whole list.
var queue = ['url/links/go/here'];
ProcessScripts(function() { // All done do what ever you want
}, 0);
function ProcessScripts(cb, index) {
getScript(queue[index], function() {
index++;
if (index === queue.length) { // Reached the end
cb();
} else {
return ProcessScripts(cb, index);
}
});
}
function getScript(script, callback) {
$.getScript(script, function() {
callback();
});
}
Solution 19 - Javascript
I have improved @adeneo script so it will load all scripts in the specified order. It doesn't do chain loading, so it's very fast, but if you want even faster, change the 50 ms wait time.
$.getMultiScripts = function(arr, path) {
function executeInOrder(scr, code, resolve) {
// if its the first script that should be executed
if (scr == arr[0]) {
arr.shift();
eval(code);
resolve();
console.log('executed', scr);
} else {
// waiting
setTimeout(function(){
executeInOrder(scr, code, resolve);
}, 50);
}
}
var _arr = $.map(arr, function(scr) {
return new Promise((resolve) => {
jQuery.ajax({
type: "GET",
url: (path || '') + scr,
dataType: "text",
success: function(code) {
console.log('loaded ', scr);
executeInOrder(scr, code, resolve);
},
cache: true
});
});
});
_arr.push($.Deferred(function( deferred ){
$( deferred.resolve );
}));
return $.when.apply($, _arr);
}
Usage is the same:
var script_arr = [
'myscript1.js',
'myscript2.js',
'myscript3.js'
];
$.getMultiScripts(script_arr, '/mypath/').done(function() {
// all scripts loaded
});