2

I have several JS and CSS files which need to be appended to the DOM dynamically with JavaScript. The method described here works fine for 1 file. However I have several of them and they should be appended/loaded in certain order:

var resources = {
  "jquery" : "jquery.js",
  "jqueryui" : "jquery_ui.js",
  "customScript" : "script.js"
}

If that matters - the resources can be in an array rather than in an object.

What I think should be done is to load each next resource in the callback of the previous one. And the callback of the last resource should call another function, which, in my case will render the HTML. However I'm not sure how to organize it with the code given in the link above. Another important aspect is that this should be done with pure JavaScript.

Any clues?

Thanks!

cycero
  • 4,075
  • 17
  • 48
  • 73

3 Answers3

4

I would suggest you to make an array of your resources rather than an object if you care about the order of their loading. I hope this solution will solve your issue.

var urls = ['https://cdnjs.cloudflare.com/ajax/libs/jquery/3.0.0-beta1/jquery.js',
  'https://cdnjs.cloudflare.com/ajax/libs/jquery/3.0.0-beta1/jquery.slim.js'
];

var i = 0;

var recursiveCallback = function() {
  if (++i < urls.length) {
    loadScript(urls[i], recursiveCallback)
  } else {
    alert('Loading Success !');
  }
}

loadScript(urls[0], recursiveCallback);

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);
}

Working Fiddle : https://jsfiddle.net/nikdtu/6uj0t0hp/

prusswan
  • 6,473
  • 3
  • 37
  • 57
Nikhil Maheshwari
  • 2,042
  • 16
  • 21
-1

I haven't tested but in concept this should work. Loop through your object. Each time you loop through create a script element then add your script to the to the source of the script element you just created. Get the last script of your resource (lastObj) and compare it with resource[key] if they are equivalent call the onload function, this will determine when the last script is loaded.

var resources = {
  "jquery": "jquery.js",
  "jqueryui": "jquery_ui.js",
  "customScript": "script.js"
}

var lastObj = resources[Object.keys(resources)[Object.keys(resources).length - 1]]

var script = [];
index = 0;
for (var key in resources) {
  script[index] = document.createElement('script');
  if (lastObj === resources[key]) {
    script[index].onload = function() {
      alert("last script loaded and ready");
    };
  }
  script[index].src = resources[key];
  document.getElementsByTagName('head')[0].appendChild(script[index]);
  index++;
}
Anthony
  • 1,339
  • 1
  • 17
  • 35
  • I think you missed the statement about certain order. You solution is using object keys and that can not always be ordered (Though in most of the cases they are). – Nikhil Maheshwari Apr 14 '16 at 18:07
  • You can refer this link for more insight. Link : http://stackoverflow.com/questions/5525795/does-javascript-guarantee-object-property-order – Nikhil Maheshwari Apr 14 '16 at 18:10
  • He doesn't seem to be adding to his object, so wouldn't it always be ordered or am I mistaken? – Anthony Apr 14 '16 at 18:18
  • I would again recommend you to read the link i posted in above comment. :) – Nikhil Maheshwari Apr 14 '16 at 18:20
  • If the OP isn't using a numeric key it would always be ordered, see [here](http://stackoverflow.com/questions/280713/elements-order-in-a-for-in-loop) but to be safe the OP should use an array instead of an object. Thanks for the insight! – Anthony Apr 14 '16 at 18:32
-1

If you don't care about old browsers you can use the following modification to load them.

index.html

<!DOCTYPE html>
<html>
<head>
    <title>Stack Overflow</title>
    <link rel="stylesheet" type="text/css" href="css/jquery-ui.min.css">
    <script type="text/javascript" src="scripts/loader.js" ></script>
</head>
<body>
    <h1>Test javascript loading strategy</h1>
    <p id="result">Loading...</p>
    <p>Date: <input type="text" id="datepicker"></p>

</body>
</html>

loader.js

function loadScript(url){

    return new Promise(function(resolve, reject) {

        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;
                    resolve();
                }
            };
        } else {  //Others
            script.onload = function(){
                resolve();
            };
        }

        script.src = url;
        document.getElementsByTagName("head")[0].appendChild(script);

    });
}

var resources = [
  "scripts/jquery.js",
  "scripts/jquery_ui.js",
  "scripts/script.js"
]

function loadAllResources() {
   return resources.reduce(function(prev, current) {

    return prev.then(function() {
      return loadScript(current);
    });

  }, Promise.resolve());
}

loadAllResources().then(function() {
  $('#result').text('Everything loaded');
});

custom script script.js

$( "#datepicker" ).datepicker();

Working JSFiddle

vl4d1m1r4
  • 1,127
  • 7
  • 17