13

I need to add prototype and then add scriptaculous and get a callback when they are both done loading. I am currently loading prototype like so:

var script = document.createElement("script");
script.src = "http://ajax.googleapis.com/ajax/libs/prototype/1.6.1.0/prototype.js";
script.onload = script.onreadystatechange = callback;
document.body.appendChild( script );

I could do this by chaining the callbacks, but that seems like poor practice ( I don't want a silly chain of 20 callback methods when I need to load more scripts). Ideas?

5
  • Why don't you add those script to a HTML insted of loading it with JavaScript? Commented Dec 8, 2009 at 12:45
  • 2
    I am making a bit of code to be pasted into other people's html to embed my stuff. I only want an empty div and a reference to my javascript file to be pasted in. Commented Dec 8, 2009 at 12:46
  • @kdiegert: this reference to your js file pasted in: you mean via <script src="my.js"></script>? Commented Dec 8, 2009 at 13:51
  • yep, so the code to be pasted in would be: <div id="myDiv"></div><script src="my.js?arguments=arguments"></script> Commented Dec 8, 2009 at 14:16
  • css-tricks.com/snippets/javascript/… Commented Nov 12, 2014 at 16:07

4 Answers 4

44

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

Sign up to request clarification or add additional context in comments.

5 Comments

aysnc.parallel or async.series might help with this github.com/caolan/async
arguments.callee throws an error in strict mode: From developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… "arguments.callee is no longer supported. In normal code arguments.callee refers to the enclosing function. This use case is weak: simply name the enclosing function! Moreover, arguments.callee substantially hinders optimizations like inlining functions, because it must be made possible to provide a reference to the un-inlined function if arguments.callee is accessed.
@GabrielLuethje, changed it to named function
Can you please verbosely document what you did there?
This solution was useful when using SinglePageApplication like Ember .
2

Because of a bug in Internet Explorer the recursive loader program from nemisj is not working correct in IE. Can be solved by setting a delay on the recursive call like:


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;  
          if(/MSIE ([6-9]+\.\d+);/.test(navigator.userAgent))window.setTimeout(function(){handler();},8,this);  
          else handler();  
        }  
        var head = document.getElementsByTagName("head")[0];  
        (head || document.body).appendChild( script );  
    };  
    (function(){  
        if(array.length!=0){  
                loader(array.shift(),arguments.callee);  
        }else{  
                callback && callback();  
        }  
    })();  
}  

This small hack does it, and often is the solution in IE, when an unexplainable problem occurs, which is too often.

Comments

0

Since scriptaculous requires prototype, you will have to chain the listeners, with whatever method you use to load these scripts.

There are various script loaders available to load scripts in parallel, as fast as possible, e.g. LABjs, but none is going to help much in this scenario.

If you end up having 10-20 scripts to load, I would recommend combining the scripts beforehand, using a tool such as a Combiner.

1 Comment

actually, this scenario is exactly what LABjs was designed to solve.
0

You can promisify the script's .onload and .onerror callbacks, then use Promise.all to wait for all of the promises to resolve before continuing with execution that is dependent on these scripts.

Here's an example:

const loadScript = url => 
  new Promise((resolve, reject) => {
    const script = document.createElement("script");
    script.src = url;
    script.onload = resolve;
    script.onerror = e => 
      reject(Error(`${url} failed to load`))
    ;
    document.head.appendChild(script);
  })
;

const scriptURLs = [
  "https://unpkg.com/htm",
  "https://unpkg.com/[email protected]/dist/axios.min.js",
  "https://cdnjs.cloudflare.com/ajax/libs/d3/6.3.1/d3.min.js",
  // ...
];

Promise.all(scriptURLs.map(loadScript))
  .then(async () => {
    /* Use your scripts here */
    console.log("proving scripts loaded:");
    axios.get("http://jsonplaceholder.typicode.com/users");
    console.log(d3.csvParse("a,b\n3,4\n5,6"));
    const html = htm.bind((t, p, ...c) => ({t, p, c}));
    console.log(html`<h1 id=hello>Hello world!</h1>`);
  })
  .catch(err => console.error(err))
;

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.