Load JavaScript on demand after lightbox is displayed

There is a button on every page of my site. When the button is clicked, a lightbox appears. The content of the lightbox is a form that relies on several JavaScript files including Angular. Those JavaScript files are used only for the form and nowhere else on the site.

In order to reduce page load time, my goal is to load the JavaScript files only after the user clicks the button.

Is there a best practice way of doing this? Some options I can think of are:

  1. The content of the lightbox is an iframe and the sourced html document contains script tags.
  2. Use the jquery.getscript method (or similar) to load the scripts

Is there a better way?

Answers


By understanding your question, You want to lazy-load your js/css/html . You can achieve this using two plugins

Require.js

Pros :

  1. Explicit dependencies between modules, listed in code.
  2. Asynchronous module loading.
  3. Lazy (on-demand) module loading.
  4. Support for one of the standard module wrappers (AMD), a lot of community modules implement it, so your modules can depend on them.

Cons :

  1. We can't inject the AngularJS modules on the fly. This loads files only when needed i.e. RequireJS only loads javascript files but it doesn't register the new angular modules and components in this new code
OcLazyLoad

Pros :

  1. OcLazyLoad is used to load the dependency files and also to inject the AngularJS modules on the fly.
  2. Loading Js and Css files are effortless.

Cons :

  1. We can't maintain the dependency files structure.

So you can use Require.js to load dependencies between modules listed in code and OcLazyLoad to inject Angular module on fly . And for your question regarding angular itself on fly, I don't think you can do that in this case.


Create script tag and attach script src and append it to head using javascript as below.

var script = document.createElement('script');
script.setAttribute('type', 'text/javascript');
script.setAttribute('src', 'test.js');
document.head.appendChild(script);

Another way is to load js file using jquery ajax call like below.

$.ajax({
    url: "test.js",
    dataType: "script",
    cache: true,
    success: function() {
        //Execute the function which you want...
    }
});

Depending on your project's architecture and whether you are using/want to use a library like require.js could help.

You can use it to require in the necessary modules for the lightbox.

The benefit of it in contrast to just dynamically loading a bunch of scripts, is that you will be able to manage dependencies between dynamically loading scripts and ensure they execute in a specific order if you need to (now or in the future).

Another idea could be to call an ajax function on the click of the button that would return an html partial, containing the lightbox scripts that you would then append to the document so the scripts would again load dynamically.


here is an example of how you achieve, I have implemented something similar on my websites and external JS are loaded only when required

<script type='text/javascript'>
$(elem).click(function() {
var d= document;
var id = 'ext-js';
if (d.getElementById(id)) return;
var t = d.createElement('script');
t.type = 'text/javascript';
t.src = '//path to external js';
t.id = id;
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(t);
});
</script>

The code also checks if the script is already loaded so it doesn't try to load with every click, you can easily scale to load multiple scripts, just give each script a different id and copy paste the below section for as many scripts you require.

var t = d.createElement('script');
t.type = 'text/javascript';
t.src = '//path to external js';
t.id = id;
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(t);

I hope this helps


Option 2 (dynamically loading scripts) is a good way to go - you probably just need to bootstrap angularjs correctly once it's been loaded. A quick demo:

(function() {
  "use strict";

  var angularLoaded = false;

  window.lazyLoadAngular = function() {
    if (angularLoaded) {
      return;
    }

    angularLoaded = true;

    var angularScript = document.createElement("script");
    angularScript.src = "//ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular.min.js";
    document.body.appendChild(angularScript);
    angularScript.onload = function() {
      var angular = window.angular,
      angularContent = document.querySelector('.angularContent');

      // reveal angular content
      angularContent.style.display = "block";

      // in practice, you're probably dynamically loading your angular app scripts as well... but here we'll just create it once angular is available
      createAngularApp();
      
      // bootstrap angular on the content
      angular.bootstrap(angularContent, ["myApp"]);
    };
  };


  function createAngularApp() {
    var angular = window.angular;
    angular.module("myApp", [])
    .controller("MyCtrl", ["$scope", MyCtrl]);
    
    function MyCtrl($scope) {
      $scope.isAngularWorking = "Yes it is!";
    }
  }

})();
.angularContent {
  display: none;
}
<button onclick="lazyLoadAngular()">Lazy load Angular</button>

<div class="angularContent">
  <div ng-controller="MyCtrl">
    Angular is working: {{isAngularWorking}}
  </div>
</div>

You can use SystemJs, Universal dynamic module loader - loads ES6 modules, AMD, CommonJS and global scripts in the browser and NodeJS, I think is very powerful engine.


For the dynamic script loading of the browser,you can use load-script package.

  • require('load-script') returns a function of the following interface: function(url[, opts][, cb]) {}
  • load-script appends a script node to the <head> element in the dom.

    Example

      var load = require('load-script')
    
      load('foo.js', function (err, script) {
      if (err) {
                // print useful message 
      }
      else {
            console.log(script.src);// Prints 'foo'.js' 
            // use script 
            // note that in IE8 and below loading error wouldn't be reported 
                }
           })
    

Alternatively you can combine the elements with the DOM

Example
 <script type="application/javascript">
   function loadJS(file) {
     // DOM: Create the script element
     var jsElement = document.createElement("script");
     // set the type attribute
     jsElement.type = "application/javascript";
     // make the script element load file
     jsElement.src = file;
     // finally insert the element to the body element in order to load the script
     document.body.appendChild(jsElement);
   }
 </script>

   <button onclick="loadJS('jsFile1.js');">Load jsFile1.js</button>
   <button onclick="loadJS('jsFile2.js');">Load jsFile2.js</button>

Need Your Help

AppEngine: Query datastore for records with no condition for a specific property

python google-app-engine google-cloud-datastore

i want to do a query that a user may or may not select a filter, but i don't want to create 2 indexes (tables).