0

To load the website faster, we need to let the DOM load before fetching non-critical assets of the site. Scenario is :

1) Optimizing CSS Delivery (loading after DOM load)

2) Removing render-blocking JavaScript (various libraries) and load them later.

3) We might have multiple pages where we have custom code which relies on libraries we are using.

4) We do not need to use requirejs or webpack as our only requirement is async loading.

So, we need a plain javascript code which can load the assets asynchronously and fire an event when all of them are loaded. That event can be listened by our custom code which uses library functions.

Gaurav
  • 1,942
  • 19
  • 31

2 Answers2

0

Our layout or header/footer can contain this code which loads specified assets and fires an event once complete.

<body>
...
<script>
// Callback after window is loaded
if (window.addEventListener) {
  window.addEventListener("load", downloadFilesAtOnload, false);
} else if (window.attachEvent) {
  window.attachEvent("onload", downloadFilesAtOnload);
} else {
  window.onload = downloadFilesAtOnload;
}

function downloadFilesAtOnload() {
  // List of Javascript files to be loaded
  var jsFiles = ["js/library1.min.js", "js/library2.min.js", "CDN.library3.min.js"];
  loadScriptArray(jsFiles, function() {
    triggerEvent(document, 'scripts_loaded');
  });

  // List of CSS files to be loaded
  var robotoFonts = "https://fonts.googleapis.com/css?family=Roboto:400,300,300italic,400italic,500,500italic,700";
  var cssFiles = ["css/cssfile1.min.css", "css/cssfile2.min.css", robotoFonts];
  for (var i = 0; i < cssFiles.length; i++) {
    var l = document.createElement('link');
    l.rel = 'stylesheet';
    l.href = cssFiles[i];
    var h = document.getElementsByTagName('head')[0];
    h.parentNode.insertBefore(l, h);
  }
}

function loadScriptArray(contentArray, contentLoadedCallback){
  var contentQuantity = contentArray.length;      //Number of Files that needs to be loaded
  var contentCompleted = 0;                       //Number of Files, that have already been loaded
  for (var i = 0; i < contentQuantity; i++) {
    loadScript(contentArray[i], function(success, path){   //This anonymous function is called everytime a script is finished
      //The only way to know which script finished, is to pass the path as an parameter
      contentCompleted++;
      if (contentCompleted == contentQuantity){    //this was the last file
        if (typeof contentLoadedCallback == 'function'){
          contentLoadedCallback();
        }
      }
    });
  }
}

function loadScript(path, scriptLoadedCallback){
  element = document.createElement("script");
  element.src = path;
  element.async = false;
  if (typeof(scriptLoadedCallback) == 'function') {  //makes the callback-function optional
    element.onload = function() {
      return scriptLoadedCallback(true, path); //true = successfull; the path is needed later
    };
    element.onerror = function() {               //you might also call the cb on error
      this.parentNode.removeChild(this);       //remove faulty node from DOM
      return scriptLoadedCallback(false, path);      //false = error; the path is needed later
    };
  }
  document.body.appendChild(element);              //insert the node in DOM (end of <head>), and load the script
}

function processEvent(el, eventName, handler) {
  if (el.addEventListener) {
    el.addEventListener(eventName, handler);
  } else if (el.attachEvent) {
    el.attachEvent('on' + eventName, function() {
      handler.call(el);
    });
  }
}

function triggerEvent(el, eventName) {
  var event;
  if (window.CustomEvent) {
    event = new CustomEvent(eventName);
  } else {
    event = document.createEvent('CustomEvent');
    event.initCustomEvent(eventName);
  }
  el.dispatchEvent(event);
}
</script>
</body>

Then, we could use javascript code which relies on external files/libraries in any of the pages like this :

<script>
processEvent(document, 'scripts_loaded', function(e) {
    // Code here
});
</script>

This answer is inspired from https://stackoverflow.com/a/36063024/3113599

Community
  • 1
  • 1
Gaurav
  • 1,942
  • 19
  • 31
0

Here is a better solution using Javascript Promises

Your layout or header/footer file can contain this code.

// Load css files
var cssFiles = [{
    type: "CDN",
    src: "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css",
    integrity: "sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u",
}, {
    type: "local",
    src: "css/cssfile1.min.css"
}, {
    type: "local",
    src: "css/cssfile2.min.css"
}, {
    type: "local",
    src: "https://fonts.googleapis.com/css?family=Roboto:400,300,300italic,400italic,500,500italic,700"
}];
var loadcssFiles = function() {
    cssFiles.forEach(function(file) {
        var style = document.createElement('link');
        style.rel = 'stylesheet';
        style.href = file.src;
        if (file.type == 'CDN') {
            style.integrity = file.integrity;
            style.crossOrigin = "anonymous";
        }
        document.head.appendChild(style);
    });
}
var raf = requestAnimationFrame || mozRequestAnimationFrame || webkitRequestAnimationFrame || msRequestAnimationFrame;
if (raf) {
    raf(loadcssFiles);
} else {
    window.addEventListener('load', loadcssFiles);
}

// Load Javascript files
var jsFiles = [{
    index: 1,
    type: "CDN",
    src: "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js",
    integrity: "sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa",
}, {
    index: 2,
    type: "local",
    src: "js/library1.min.js"
}, {
    index: 3,
    type: "local",
    src: "js/library2.min.js"
}];
var loadjsFiles = new Promise(function(resolve, reject) {
    var failed;
    jsFiles.forEach(function(file) {
        var script = document.createElement('script');
        script.src = file.src;
        script.onerror = function() {
            failed = true;
            reject(this);
        }
        script.async = false;
        if (file.type == 'CDN') {
            script.integrity = file.integrity;
            script.crossOrigin = "anonymous";
        }
        document.head.appendChild(script);
        script.onload = script.onreadystatechange = function() {
            if (!failed && (!this.readyState || this.readyState == "complete") && file.index == jsFiles.length ) {
                resolve(this);
            }
        }
    });
});

Then, we could use javascript code which relies on external files/libraries in any of the pages like this :

loadjsFiles.then(function() {
    // Code here
});
Gaurav
  • 1,942
  • 19
  • 31