Asynchronous Script Loading Callback
Solution 1
Thanks RASG for https://stackoverflow.com/a/3211647/982924
Async function with callback:
function async(u, c) {
var d = document, t = 'script',
o = d.createElement(t),
s = d.getElementsByTagName(t)[0];
o.src = '//' + u;
if (c) { o.addEventListener('load', function (e) { c(null, e); }, false); }
s.parentNode.insertBefore(o, s);
}
Usage:
async('snapabug.appspot.com/snapabug.js', function() {
SnapABug.init('XXXXX-XXXXX-XXXXX-XXXXX-XXXXX');
});
Solution 2
A more recent snippet:
async function loadAsync(src) {
const script = document.createElement('script');
script.src = src;
return new Promise((resolve, reject) => {
script.onreadystatechange = function () {
if (script.readyState === 'loaded' || script.readyState === 'complete') {
script.onreadystatechange = null;
resolve(true);
}
};
document.getElementsByTagName('head')[0].appendChild(script);
});
}
utilisation
loadAsync(`https://....js`).then(_ => {
// ... script loaded here
})
James Kyle's answer doesn't take IE9 into account. Here is a modified version of the code I found in the link proposed in the comments. Modify the var baseUrl so it can find the script accordingly.
//for requiring a script loaded asynchronously.
function loadAsync(src, callback, relative){
var baseUrl = "/resources/script/";
var script = document.createElement('script');
if(relative === true){
script.src = baseUrl + src;
}else{
script.src = src;
}
if(callback !== null){
if (script.readyState) { // IE, incl. IE9
script.onreadystatechange = function() {
if (script.readyState === "loaded" || script.readyState === "complete") {
script.onreadystatechange = null;
callback();
}
};
} else {
script.onload = function() { // Other browsers
callback();
};
}
}
document.getElementsByTagName('head')[0].appendChild(script);
}
utilisation:
loadAsync('https://www.gstatic.com/charts/loader.js' , function(){
chart.loadCharts();
});
// OR relative path
loadAsync('fastclick.js', null, true);
Solution 3
The other answers works well, but aren't super readable or require Promises. Here is my two cents:
function loadScript(src, callback) {
var script = document.createElement('script');
script.setAttribute('src', src);
script.addEventListener('load', callback);
document.head.appendChild(script);
},
James Kyle
Babel & Flow conspirator. Lerna maintainer. Engineer at Facebook. Previously CloudFlare. The Real Beyonce of JavaScript.
Updated on April 14, 2020Comments
-
James Kyle about 4 years
I've created a short function based on Mathias Bynens Optimization of the Google Analytics asynchronous script that goes as following:
function async(src) { var d = document, t = 'script', o = d.createElement(t), s = d.getElementsByTagName(t)[0]; o.src = '//' + src; s.parentNode.insertBefore(o, s); }
This works great and I've already started using it for several different scripts
// Crazy Egg async('dnn506yrbagrg.cloudfront.net/pages/scripts/XXXXX/XXXXX.js?' + Math.floor(new Date().getTime() / 3600000)); // User Voice var uvOptions = {}; async('widget.uservoice.com/XXXXX.js'); // Google Analytics var _gaq = [['_setAccount', 'UA-XXXXX-XX'], ['_setDomainName', 'coachup.com'], ['_trackPageview']]; async('google-analytics.com/ga.js'); // Stripe async('js.stripe.com/v1');
The problem comes when I encounter a script that needs to be called after it's loaded:
// Snap Engage async('snapabug.appspot.com/snapabug.js'); SnapABug.init('XXXXX-XXXXX-XXXXX-XXXXX-XXXXX');
So I figured I'd turn this into a callback function that would be used as so:
async('snapabug.appspot.com/snapabug.js', function() { SnapABug.init('XXXXX-XXXXX-XXXXX-XXXXX-XXXXX'); });
I did not expect that this would be difficult for me to do but it has turned out that way.
My question is what is the most efficient way to add a callback without overcomplicating the code.
See the jsfiddle: http://jsfiddle.net/JamesKyle/HQDu6/
-
RASG over 11 years
-
-
aefxx about 10 yearsThe line
o.u = '//' + u;
should reado.src = '//' + u;
or else it won't load a single byte. Minifying can be tricky at times. -
baohouse over 9 yearss.addEventListener should be o.addEventListener
-
Dave Cahill about 9 yearsI think this needs more logic to deal with IE8 and before, see for example: aaronpeters.nl/blog/prevent-double-callback-execution-in-IE9
-
Trev14 about 7 yearsNice work & thanks for the great answer. Just change "doc" in the last line of the function to "document".
-
Kevin Farrugia over 6 yearsI think this solution is more comprehensive and includes a
Promise
implementation too: stackoverflow.com/questions/7718935/load-scripts-asynchronously