193

How can you reliably and dynamically load a JavaScript file? This will can be used to implement a module or component that when 'initialized' the component will dynamically load all needed JavaScript library scripts on demand.

The client that uses the component isn't required to load all the library script files (and manually insert <script> tags into their web page) that implement this component - just the 'main' component script file.

How do mainstream JavaScript libraries accomplish this (Prototype, jQuery, etc)? Do these tools merge multiple JavaScript files into a single redistributable 'build' version of a script file? Or do they do any dynamic loading of ancillary 'library' scripts?

An addition to this question: is there a way to handle the event after a dynamically included JavaScript file is loaded? Prototype has document.observe for document-wide events. Example:

document.observe("dom:loaded", function() {
  // initially hide all containers for tab content
  $$('div.tabcontent').invoke('hide');
});

What are the available events for a script element?

Cole Tobin
  • 9,206
  • 15
  • 49
  • 74
Adam
  • 28,537
  • 15
  • 60
  • 73

29 Answers29

91

There is no import / include / require in javascript, but there are two main ways to achieve what you want:

1 - You can load it with an AJAX call then use eval.

This is the most straightforward way but it's limited to your domain because of the Javascript safety settings, and using eval is opening the door to bugs and hacks.

2 - Add a script element with the script URL in the HTML.

Definitely the best way to go. You can load the script even from a foreign server, and it's clean as you use the browser parser to evaluate the code. You can put the script element in the head element of the web page, or at the bottom of the body.

Both of these solutions are discussed and illustrated here.

Now, there is a big issue you must know about. Doing that implies that you remotely load the code. Modern web browsers will load the file and keep executing your current script because they load everything asynchronously to improve performances.

It means that if you use these tricks directly, you won't be able to use your newly loaded code the next line after you asked it to be loaded, because it will be still loading.

E.G : my_lovely_script.js contains MySuperObject

var js = document.createElement("script");

js.type = "text/javascript";
js.src = jsFilePath;

document.body.appendChild(js);

var s = new MySuperObject();

Error : MySuperObject is undefined

Then you reload the page hitting F5. And it works! Confusing...

So what to do about it ?

Well, you can use the hack the author suggests in the link I gave you. In summary, for people in a hurry, he uses en event to run a callback function when the script is loaded. So you can put all the code using the remote library in the callback function. E.G :

function loadScript(url, callback)
{
    // adding the script element to the head as suggested before
   var head = document.getElementsByTagName('head')[0];
   var script = document.createElement('script');
   script.type = 'text/javascript';
   script.src = url;

   // then bind the event to the callback function 
   // there are several events for cross browser compatibility
   script.onreadystatechange = callback;
   script.onload = callback;

   // fire the loading
   head.appendChild(script);
}

Then you write the code you want to use AFTER the script is loaded in a lambda function :

var myPrettyCode = function() {
    // here, do what ever you want
};

Then you run all that :

loadScript("my_lovely_script.js", myPrettyCode);

Ok, I got it. But it's a pain to write all this stuff.

Well, in that case, you can use as always the fantastic free jQuery framework, which let you do the very same thing in one line :

$.getScript("my_lovely_script.js", function() {
    alert("Script loaded and executed.");
    // here you can use anything you defined in the loaded script
});
bignose
  • 30,281
  • 14
  • 77
  • 110
  • This works. I hope sharing my experience here saves your time because I spent many hours on this. I'm using Angular 6 and applied template (html,css,jquery) The problem is the template has a js file which loads after the html elements are loaded to attach listeners events. That js file was hard to executre after angular app is loaded. Adding it to angular app ( angular.json) scripts tag will bundle them but not execute that js file after loading. It is too much code to rewrite in typescript so this was great help. Next comment I'll put the example becuse of comment length – Nour Lababidi Sep 23 '18 at 20:19
  • I simply used this code as follow: ngAfterViewInit() { debugger; $.getScript("/assets/js/jquery.app.js", function() { alert("Script loaded and executed."); // here you can use anything you defined in the loaded script }); } – Nour Lababidi Sep 23 '18 at 20:20
  • For the '$' in angular I followed this : https://stackoverflow.com/questions/32050645/how-to-use-jquery-with-typescript – Nour Lababidi Sep 23 '18 at 20:21
  • 1
    Portions of this post seem plagiarized from https://stackoverflow.com/a/950146/6243352 – ggorlen Dec 16 '20 at 22:50
  • It works. Finally I solved my CSP nonce problem with it. Thank you. – handaru Jun 28 '21 at 15:46
88

You may create a script element dynamically, using Prototypes:

new Element("script", {src: "myBigCodeLibrary.js", type: "text/javascript"});

The problem here is that we do not know when the external script file is fully loaded.

We often want our dependant code on the very next line and like to write something like:

if (iNeedSomeMore) {
    Script.load("myBigCodeLibrary.js"); // includes code for myFancyMethod();
    myFancyMethod(); // cool, no need for callbacks!
}

There is a smart way to inject script dependencies without the need of callbacks. You simply have to pull the script via a synchronous AJAX request and eval the script on global level.

If you use Prototype the Script.load method looks like this:

var Script = {
    _loadedScripts: [],
    include: function(script) {
        // include script only once
        if (this._loadedScripts.include(script)) {
            return false;
        }
        // request file synchronous
        var code = new Ajax.Request(script, {
            asynchronous: false,
            method: "GET",
            evalJS: false,
            evalJSON: false
        }).transport.responseText;
        // eval code on global level
        if (Prototype.Browser.IE) {
            window.execScript(code);
        } else if (Prototype.Browser.WebKit) {
            $$("head").first().insert(Object.extend(
                new Element("script", {
                    type: "text/javascript"
                }), {
                    text: code
                }
            ));
        } else {
            window.eval(code);
        }
        // remember included script
        this._loadedScripts.push(script);
    }
};
bignose
  • 30,281
  • 14
  • 77
  • 110
aemkei
  • 11,076
  • 8
  • 37
  • 29
  • 1
    What can I do to get it working for cross-domain? *(loading script from `http://web.archive.org/web/20140905044059/http://www.howtocreate.co.uk/operaStu‌​ff/userjs/aagmfunctions.js`)* – user2284570 Sep 14 '14 at 02:28
  • 1
    @user2284570 CORS http://stackoverflow.com/questions/5750696/how-to-get-a-cross-origin-resource-sharing-cors-post-request-working – Ciasto piekarz Jan 21 '17 at 10:29
  • 1
    @Ciastopiekarz : I don’t control`web.archive.org`. – user2284570 Jan 21 '17 at 19:31
  • Then you have to scrape the data you want to access using some other program and provide it to yourself – David Schumann Dec 03 '17 at 13:21
29

I used a much less complicated version recently with jQuery:

<script src="scripts/jquery.js"></script>
<script>
  var js = ["scripts/jquery.dimensions.js", "scripts/shadedborder.js", "scripts/jqmodal.js", "scripts/main.js"];
  var $head = $("head");
  for (var i = 0; i < js.length; i++) {
    $head.append("<script src=\"" + js[i] + "\"></scr" + "ipt>");
  }
</script>

It worked great in every browser I tested it in: IE6/7, Firefox, Safari, Opera.

Update: jQuery-less version:

<script>
  var js = ["scripts/jquery.dimensions.js", "scripts/shadedborder.js", "scripts/jqmodal.js", "scripts/main.js"];
  for (var i = 0, l = js.length; i < l; i++) {
    document.getElementsByTagName("head")[0].innerHTML += ("<script src=\"" + js[i] + "\"></scr" + "ipt>");
  }
</script>
Clomp
  • 3,168
  • 2
  • 23
  • 36
travis
  • 35,751
  • 21
  • 71
  • 94
  • 29
    That's great... unless you're trying to load jquery. –  Feb 12 '10 at 08:58
  • 1
    Looks like jQuery is going to roll in the Require plugin into jQuery Core for a future release: http://plugins.jquery.com/project/require – Adam Feb 10 '11 at 16:47
  • 1
    An even better way to use jQuery is to use `$.getScript`. See my answer. – Muhd Feb 13 '12 at 20:39
  • 1
    Modernizr (yepnope.js) or lab.js are the appropriate solutions for this. using a heavy script library (which must load first) is not the most suitable answer for mobile or many other situations. – 1nfiniti Feb 14 '12 at 14:53
  • nice answer but why write a for loop on 1 line?! – JonnyRaa Feb 12 '14 at 11:17
  • 1
    @JonnyLeeds good point, I was being too compact, I'll update. – travis Feb 12 '14 at 16:19
  • 2
    @MaulikGangani older browsers and html validators would interpret that as the token to end the script. – travis Apr 15 '17 at 18:55
19

I did basically the same thing that you did Adam, but with a slight modification to make sure I was appending to the head element to get the job done. I simply created an include function (code below) to handle both script and CSS files.

This function also checks to make sure that the script or CSS file hasn't already been loaded dynamically. It does not check for hand coded values and there may have been a better way to do that, but it served the purpose.

function include( url, type ){
    // First make sure it hasn't been loaded by something else.
    if( Array.contains( includedFile, url ) )
        return;
     
    // Determine the MIME type.
    var jsExpr = new RegExp( "js$", "i" );
    var cssExpr = new RegExp( "css$", "i" );
    if( type == null )
        if( jsExpr.test( url ) )
            type = 'text/javascript';
        else if( cssExpr.test( url ) )
            type = 'text/css';
            
    // Create the appropriate element.
    var element = null;
    switch( type ){
        case 'text/javascript' :
            element = document.createElement( 'script' );
            element.type = type;
            element.src = url;
            break;
        case 'text/css' :
            element = document.createElement( 'link' );
            element.rel = 'stylesheet';
            element.type = type;
            element.href = url;
            break;
    }
    
    // Insert it to the <head> and the array to ensure it is not
    // loaded again.
    document.getElementsByTagName("head")[0].appendChild( element );
    Array.add( includedFile, url );
}
bignose
  • 30,281
  • 14
  • 77
  • 110
palehorse
  • 26,407
  • 4
  • 40
  • 48
  • Without more context than that Pickle, I'm afraid I don't have any suggestions. The code above works as-is, it was pulled directly from a functioning project. – palehorse Feb 12 '10 at 15:30
  • 6
    @palehorse, Mike and Muhd, are right, it might work in your project, but thats because the "includedFile" and "Array" variables must be defined elsewhere in your project, this code on its own won't run, it might be better to edit it so it can work outside the context of your project, or at least add a comment explaining what those undefined variables are (type etc) – user280109 Oct 23 '14 at 10:21
13

another awesome answer

$.getScript("my_lovely_script.js", function(){


   alert("Script loaded and executed.");
  // here you can use anything you defined in the loaded script

 });

https://stackoverflow.com/a/950146/671046

Community
  • 1
  • 1
Naveed
  • 11,057
  • 2
  • 44
  • 63
  • 2
    What can I do to get it working for cross-domain? *(loading script from `http://web.archive.org/web/20140905044059/http://www.howtocreate.co.uk/operaStu‌​ff/userjs/aagmfunctions.js`)* – user2284570 Sep 14 '14 at 02:32
  • If the script contains errors they will not be shown in console. – Yan King Yin Dec 23 '22 at 08:22
10

Dynamic module import landed in Firefox 67+.

(async () => {
   await import('./synth/BubbleSynth.js')
})()

With error handling:

(async () => {
    await import('./synth/BubbleSynth.js').catch((error) => console.log('Loading failed' + error))
})()

It also works for any kind of non-modules libraries, on this case the lib is available on the window.self object, the old way, but only on demand, which is nice.

Example using suncalc.js, the server must have CORS enabled to works this way!

(async () => {
 await import('https://cdnjs.cloudflare.com/ajax/libs/suncalc/1.8.0/suncalc.min.js')
 .then( () => {
   let times = SunCalc.getTimes(new Date(), 51.5,-0.1);
   console.log("Golden Hour today in London: " + times.goldenHour.getHours() + ':' + times.goldenHour.getMinutes() + ". Take your pics!")
  })
})()

https://caniuse.com/#feat=es6-module-dynamic-import

NVRM
  • 11,480
  • 1
  • 88
  • 87
  • This works in every browser having received updates in the past two years and is much cleaner than the – Marcel Waldvogel Dec 11 '21 at 10:44
  • 1
    If loading a file with a relative path, use **./** e.g. import('./you_path/your_file.js') – Ludo Apr 28 '22 at 22:48
9

Here is some example code I've found... does anyone have a better way?

  function include(url)
  {
    var s = document.createElement("script");
    s.setAttribute("type", "text/javascript");
    s.setAttribute("src", url);
    var nodes = document.getElementsByTagName("*");
    var node = nodes[nodes.length -1].parentNode;
    node.appendChild(s);
  }
Adam
  • 28,537
  • 15
  • 60
  • 73
  • What can I do to get it working for cross-domain? *(loading script from `http://web.archive.org/web/20140905044059/http://www.howtocreate.co.uk/operaStu‌​ff/userjs/aagmfunctions.js`)* – user2284570 Sep 14 '14 at 02:32
5

If you have jQuery loaded already, you should use $.getScript.

This has an advantage over the other answers here in that you have a built in callback function (to guarantee the script is loaded before the dependant code runs) and you can control caching.

Muhd
  • 24,305
  • 22
  • 61
  • 78
3

If you want a SYNC script loading, you need to add script text directly to HTML HEAD element. Adding it as will trigger an ASYNC load. To load script text from external file synchronously, use XHR. Below a quick sample (it is using parts of other answers in this and other posts):

/*sample requires an additional method for array prototype:*/

if (Array.prototype.contains === undefined) {
Array.prototype.contains = function (obj) {
    var i = this.length;
    while (i--) { if (this[i] === obj) return true; }
    return false;
};
};

/*define object that will wrap our logic*/
var ScriptLoader = {
LoadedFiles: [],

LoadFile: function (url) {
    var self = this;
    if (this.LoadedFiles.contains(url)) return;

    var xhr = new XMLHttpRequest();
    xhr.onload = function () {
        if (xhr.readyState === 4) {
            if (xhr.status === 200) {
                self.LoadedFiles.push(url);
                self.AddScript(xhr.responseText);
            } else {
                if (console) console.error(xhr.statusText);
            }
        }
    };
    xhr.open("GET", url, false);/*last parameter defines if call is async or not*/
    xhr.send(null);
},

AddScript: function (code) {
    var oNew = document.createElement("script");
    oNew.type = "text/javascript";
    oNew.textContent = code;
    document.getElementsByTagName("head")[0].appendChild(oNew);
}
};

/*Load script file. ScriptLoader will check if you try to load a file that has already been loaded (this check might be better, but I'm lazy).*/

ScriptLoader.LoadFile("Scripts/jquery-2.0.1.min.js");
ScriptLoader.LoadFile("Scripts/jquery-2.0.1.min.js");
/*this will be executed right after upper lines. It requires jquery to execute. It requires a HTML input with id "tb1"*/
$(function () { alert($('#tb1').val()); });
bignose
  • 30,281
  • 14
  • 77
  • 110
Sielu
  • 1,070
  • 14
  • 19
3

With Promises you can simplify it like this. Loader function:

  const loadCDN = src =>
    new Promise((resolve, reject) => {
      if (document.querySelector(`head > script[src="${src}"]`) !== null) return resolve()
      const script = document.createElement("script")
      script.src = src
      script.async = true
      document.head.appendChild(script)
      script.onload = resolve
      script.onerror = reject
    })

Usage (async/await):

await loadCDN("https://.../script.js")

Usage (Promise):

loadCDN("https://.../script.js").then(res => {}).catch(err => {})

NOTE: there was one similar solution but it doesn't check if the script is already loaded and loads the script each time. This one checks src property.

radulle
  • 1,437
  • 13
  • 19
  • This function can fire resolve() before script is actually loaded. – vatavale May 01 '22 at 18:08
  • Actually resolve is called only once script is loaded. Ofcourse, if your environment allows using dynamic module imports is optimal solution. – radulle May 02 '22 at 06:34
  • Yes, some of the functionality is there. But in two cases, this function can give false results - if you call it twice quickly and if the document had a tag with the script from the beginning and the function was called before the script was loaded. If these scenarios are excluded, then it's fine. – vatavale May 02 '22 at 10:55
2

does anyone have a better way?

I think just adding the script to the body would be easier then adding it to the last node on the page. How about this:

function include(url) {
  var s = document.createElement("script");
  s.setAttribute("type", "text/javascript");
  s.setAttribute("src", url);
  document.body.appendChild(s);
}
Joseph Pecoraro
  • 2,858
  • 1
  • 20
  • 25
  • What can I do to get it working for cross-domain? *(loading script from `http://web.archive.org/web/20140905044059/http://www.howtocreate.co.uk/operaStu‌​ff/userjs/aagmfunctions.js`)* – user2284570 Sep 14 '14 at 02:34
2

i've used yet another solution i found on the net ... this one is under creativecommons and it checks if the source was included prior to calling the function ...

you can find the file here: include.js

/** include - including .js files from JS - bfults@gmail.com - 2005-02-09
 ** Code licensed under Creative Commons Attribution-ShareAlike License 
 ** http://creativecommons.org/licenses/by-sa/2.0/
 **/              
var hIncludes = null;
function include(sURI)
{   
  if (document.getElementsByTagName)
  {   
    if (!hIncludes)
    {
      hIncludes = {}; 
      var cScripts = document.getElementsByTagName("script");
      for (var i=0,len=cScripts.length; i < len; i++)
        if (cScripts[i].src) hIncludes[cScripts[i].src] = true;
    }
    if (!hIncludes[sURI])
    {
      var oNew = document.createElement("script");
      oNew.type = "text/javascript";
      oNew.src = sURI;
      hIncludes[sURI]=true;
      document.getElementsByTagName("head")[0].appendChild(oNew);
    }
  }   
} 
Pierre Spring
  • 10,525
  • 13
  • 49
  • 44
  • What can I do to get it working for cross-domain? *(loading script from `http://web.archive.org/web/20140905044059/http://www.howtocreate.co.uk/operaStu‌​ff/userjs/aagmfunctions.js`)* – user2284570 Sep 14 '14 at 02:33
2

I know my answer is bit late for this question, but, here is a great article in www.html5rocks.com - Deep dive into the murky waters of script loading .

In that article it is concluded that in regards of browser support, the best way to dynamically load JavaScript file without blocking content rendering is the following way:

Considering you've four scripts named script1.js, script2.js, script3.js, script4.js then you can do it with applying async = false:

[
  'script1.js',
  'script2.js',
  'script3.js',
  'script4.js'
].forEach(function(src) {
  var script = document.createElement('script');
  script.src = src;
  script.async = false;
  document.head.appendChild(script);
});

Now, Spec says: Download together, execute in order as soon as all download.

Firefox < 3.6, Opera says: I have no idea what this “async” thing is, but it just so happens I execute scripts added via JS in the order they’re added.

Safari 5.0 says: I understand “async”, but don’t understand setting it to “false” with JS. I’ll execute your scripts as soon as they land, in whatever order.

IE < 10 says: No idea about “async”, but there is a workaround using “onreadystatechange”.

Everything else says: I’m your friend, we’re going to do this by the book.

Now, the full code with IE < 10 workaround:

var scripts = [
  'script1.js',
  'script2.js',
  'script3.js',
  'script4.js'
];
var src;
var script;
var pendingScripts = [];
var firstScript = document.scripts[0];

// Watch scripts load in IE
function stateChange() {
  // Execute as many scripts in order as we can
  var pendingScript;
  while (pendingScripts[0] && pendingScripts[0].readyState == 'loaded') {
    pendingScript = pendingScripts.shift();
    // avoid future loading events from this script (eg, if src changes)
    pendingScript.onreadystatechange = null;
    // can't just appendChild, old IE bug if element isn't closed
    firstScript.parentNode.insertBefore(pendingScript, firstScript);
  }
}

// loop through our script urls
while (src = scripts.shift()) {
  if ('async' in firstScript) { // modern browsers
    script = document.createElement('script');
    script.async = false;
    script.src = src;
    document.head.appendChild(script);
  }
  else if (firstScript.readyState) { // IE<10
    // create a script and add it to our todo pile
    script = document.createElement('script');
    pendingScripts.push(script);
    // listen for state changes
    script.onreadystatechange = stateChange;
    // must set src AFTER adding onreadystatechange listener
    // else we’ll miss the loaded event for cached scripts
    script.src = src;
  }
  else { // fall back to defer
    document.write('<script src="' + src + '" defer></'+'script>');
  }
}

A few tricks and minification later, it’s 362 bytes

!function(e,t,r){function n(){for(;d[0]&&"loaded"==d[0][f];)c=d.shift(),c[o]=!i.parentNode.insertBefore(c,i)}for(var s,a,c,d=[],i=e.scripts[0],o="onreadystatechange",f="readyState";s=r.shift();)a=e.createElement(t),"async"in i?(a.async=!1,e.head.appendChild(a)):i[f]?(d.push(a),a[o]=n):e.write("<"+t+' src="'+s+'" defer></'+t+">"),a.src=s}(document,"script",[
  "//other-domain.com/1.js",
  "2.js"
])
asmmahmud
  • 4,844
  • 2
  • 40
  • 47
2

There's a new proposed ECMA standard called dynamic import, recently incorporated into Chrome and Safari.

const moduleSpecifier = './dir/someModule.js';

import(moduleSpecifier)
   .then(someModule => someModule.foo()); // executes foo method in someModule
Alister
  • 27,049
  • 9
  • 40
  • 35
2

Just found out about a great feature in YUI 3 (at the time of writing available in preview release). You can easily insert dependencies to YUI libraries and to "external" modules (what you are looking for) without too much code: YUI Loader.

It also answers your second question regarding the function being called as soon as the external module is loaded.

Example:

YUI({
    modules: {
        'simple': {
            fullpath: "http://example.com/public/js/simple.js"
        },
        'complicated': {
            fullpath: "http://example.com/public/js/complicated.js"
            requires: ['simple']  // <-- dependency to 'simple' module
        }
    },
    timeout: 10000
}).use('complicated', function(Y, result) {
    // called as soon as 'complicated' is loaded
    if (!result.success) {
        // loading failed, or timeout
        handleError(result.msg);
    } else {
        // call a function that needs 'complicated'
        doSomethingComplicated(...);
    }
});

Worked perfectly for me and has the advantage of managing dependencies. Refer to the YUI documentation for an example with YUI 2 calendar.

Kariem
  • 4,398
  • 3
  • 44
  • 73
  • This is probably ideal except for YUI being obscenely large for just this functionality. – Muhd Feb 13 '12 at 20:06
1

The technique we use at work is to request the javascript file using an AJAX request and then eval() the return. If you're using the prototype library, they support this functionality in their Ajax.Request call.

17 of 26
  • 27,121
  • 13
  • 66
  • 85
1

Keep it nice, short, simple, and maintainable! :]

// 3rd party plugins / script (don't forget the full path is necessary)
var FULL_PATH = '', s =
[
    FULL_PATH + 'plugins/script.js'      // Script example
    FULL_PATH + 'plugins/jquery.1.2.js', // jQuery Library 
    FULL_PATH + 'plugins/crypto-js/hmac-sha1.js',      // CryptoJS
    FULL_PATH + 'plugins/crypto-js/enc-base64-min.js'  // CryptoJS
];

function load(url)
{
    var ajax = new XMLHttpRequest();
    ajax.open('GET', url, false);
    ajax.onreadystatechange = function ()
    {
        var script = ajax.response || ajax.responseText;
        if (ajax.readyState === 4)
        {
            switch(ajax.status)
            {
                case 200:
                    eval.apply( window, [script] );
                    console.log("library loaded: ", url);
                    break;
                default:
                    console.log("ERROR: library not loaded: ", url);
            }
        }
    };
    ajax.send(null);
}

 // initialize a single load 
load('plugins/script.js');

// initialize a full load of scripts
if (s.length > 0)
{
    for (i = 0; i < s.length; i++)
    {
        load(s[i]);
    }
}

This code is simply a short functional example that could require additional feature functionality for full support on any (or given) platform.

tfont
  • 10,891
  • 7
  • 56
  • 52
1

jquery resolved this for me with its .append() function - used this to load the complete jquery ui package

/*
 * FILENAME : project.library.js
 * USAGE    : loads any javascript library
 */
    var dirPath = "../js/";
    var library = ["functions.js","swfobject.js","jquery.jeditable.mini.js","jquery-ui-1.8.8.custom.min.js","ui/jquery.ui.core.min.js","ui/jquery.ui.widget.min.js","ui/jquery.ui.position.min.js","ui/jquery.ui.button.min.js","ui/jquery.ui.mouse.min.js","ui/jquery.ui.dialog.min.js","ui/jquery.effects.core.min.js","ui/jquery.effects.blind.min.js","ui/jquery.effects.fade.min.js","ui/jquery.effects.slide.min.js","ui/jquery.effects.transfer.min.js"];

    for(var script in library){
        $('head').append('<script type="text/javascript" src="' + dirPath + library[script] + '"></script>');
    }

To Use - in the head of your html/php/etc after you import jquery.js you would just include this one file like so to load in the entirety of your library appending it to the head...

<script type="text/javascript" src="project.library.js"></script>
JM Design
  • 97
  • 3
1

Something like this...

<script>
     $(document).ready(function() {
          $('body').append('<script src="https://maps.googleapis.com/maps/api/js?key=KEY&libraries=places&callback=getCurrentPickupLocation" async defer><\/script>');
     });
</script>
James Arnold
  • 698
  • 3
  • 9
  • 22
1

This works:

await new Promise((resolve, reject) => {
  let js = document.createElement("script"); 
  js.src = "mylibrary.js"; 
  js.onload = resolve; 
  js.onerror = reject; 
  document.body.appendChild(js)
});

Obviously if the script you want to import is a module, you can use the import(...) function.

Redoman
  • 3,059
  • 3
  • 34
  • 62
1

In as much as I love how handy the JQuery approach is, the JavaScript approach isn't that complicated but just require little tweaking to what you already use... Here is how I load JS dynamically(Only when needed), and wait for them to load before executing the script that depends on them.

JavaScript Approach

//Create a script element that will load
let dynamicScript = document.createElement('script');

//Set source to the script we need to load
dynamicScript.src = 'linkToNeededJsFile.js';

//Set onload to callback function that depends on this script or do inline as shown below
dynamicScript.onload = () => {

    //Code that depends on the loaded script should be here

};

//append the created script element to body element
document.body.append(dynamicScript);

There are other ways approach one could accomplish this with JS but, I prefer this as it's require the basic JS knowledge every dev can relate.

Not part of the answer but here is the JQuery version I prefer with projects that already include JQuery:

$.getScript('linkToNeededJsFile.js', () => {

    //Code that depends on the loaded script should be here

});

More on the JQuery option here

Enrico
  • 121
  • 2
  • 9
0

I am lost in all these samples but today I needed to load an external .js from my main .js and I did this:

document.write("<script src='https://www.google.com/recaptcha/api.js'></script>");
adrianTNT
  • 3,671
  • 5
  • 29
  • 35
  • you can take a look at the link: [answer](https://stackoverflow.com/questions/40808425/how-load-and-use-dynamically-javascript/45489130#45489130) – asmmahmud Aug 03 '17 at 15:59
0

Here is a simple one with callback and IE support:

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

loadScript("https://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js", function () {

     //jQuery loaded
     console.log('jquery loaded');

});
Jacob
  • 3,598
  • 4
  • 35
  • 56
0

Here a simple example for a function to load JS files. Relevant points:

  • you don't need jQuery, so you may use this initially to load also the jQuery.js file
  • it is async with callback
  • it ensures it loads only once, as it keeps an enclosure with the record of loaded urls, thus avoiding usage of network
  • contrary to jQuery $.ajax or $.getScript you can use nonces, solving thus issues with CSP unsafe-inline. Just use the property script.nonce
var getScriptOnce = function() {

    var scriptArray = []; //array of urls (closure)

    //function to defer loading of script
    return function (url, callback){
        //the array doesn't have such url
        if (scriptArray.indexOf(url) === -1){

            var script=document.createElement('script');
            script.src=url;
            var head=document.getElementsByTagName('head')[0],
                done=false;

            script.onload=script.onreadystatechange = function(){
                if ( !done && (!this.readyState || this.readyState == 'loaded' || this.readyState == 'complete') ) {
                    done=true;
                    if (typeof callback === 'function') {
                        callback();
                    }
                    script.onload = script.onreadystatechange = null;
                    head.removeChild(script);

                    scriptArray.push(url);
                }
            };

            head.appendChild(script);
        }
    };
}();

Now you use it simply by

getScriptOnce("url_of_your_JS_file.js");
João Pimentel Ferreira
  • 14,289
  • 10
  • 80
  • 109
0

For those of you, who love one-liners:

import('./myscript.js');

Chances are you might get an error, like:

Access to script at 'http://..../myscript.js' from origin 'http://127.0.0.1' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

In which case, you can fallback to:

fetch('myscript.js').then(r => r.text()).then(t => new Function(t)());
Ludmil Tinkov
  • 555
  • 5
  • 4
  • 1
    Is there a reason to use the `Function` constructor instead of `eval`? From the MDN documentation `Function` just creates a function and takes parameter names then the function body as arguments. Whereas `eval` just takes a string of code, which is what you'd have in a .js file. – Michael Hoffmann Dec 09 '20 at 17:02
  • @MichaelHoffmann, as far as I remember, I read somewhere that Function was a bit safer than eval, but I'll have to double-check that. – Ludmil Tinkov Jun 01 '21 at 03:46
  • This is useful for local, but may create some XSS weakness otherwise. On this case, both `eval` and `Function` are not safe. – NVRM Jul 07 '21 at 17:11
0

This function uses memorization. And could be called many times with no conflicts of loading and running the same script twice. Also it's not resolving sooner than the script is actually loaded (like in @radulle answer).

const loadScript = function () {
    let cache = {};
    return function (src) {
        return cache[src] || (cache[src] = new Promise((resolve, reject) => {
            let s = document.createElement('script');
            s.defer = true;
            s.src = src;
            s.onload = resolve;
            s.onerror = reject;
            document.head.append(s);
        }));
    }
}();

Please notice the parentheses () after the function expression.

Parallel loading of scripts:

Promise.all([
    loadScript('/script1.js'),
    loadScript('/script2.js'),
    // ...
]).then(() => {
    // do something
})

You can use the same method for dynamic loading stylesheets.

vatavale
  • 1,470
  • 22
  • 31
0

There are scripts that are designed specifically for this purpose.

yepnope.js is built into Modernizr, and lab.js is a more optimized (but less user friendly version.

I wouldn't reccomend doing this through a big library like jquery or prototype - because one of the major benefits of a script loader is the ability to load scripts early - you shouldn't have to wait until jquery & all your dom elements load before running a check to see if you want to dynamically load a script.

1nfiniti
  • 2,032
  • 14
  • 19
-1

all the major javascript libraries like jscript, prototype, YUI have support for loading script files. For example, in YUI, after loading the core you can do the following to load the calendar control

var loader = new YAHOO.util.YUILoader({

    require: ['calendar'], // what components?

    base: '../../build/',//where do they live?

    //filter: "DEBUG",  //use debug versions (or apply some
                        //some other filter?

    //loadOptional: true, //load all optional dependencies?

    //onSuccess is the function that YUI Loader
    //should call when all components are successfully loaded.
    onSuccess: function() {
        //Once the YUI Calendar Control and dependencies are on
        //the page, we'll verify that our target container is 
        //available in the DOM and then instantiate a default
        //calendar into it:
        YAHOO.util.Event.onAvailable("calendar_container", function() {
            var myCal = new YAHOO.widget.Calendar("mycal_id", "calendar_container");
            myCal.render();
        })
     },

    // should a failure occur, the onFailure function will be executed
    onFailure: function(o) {
        alert("error: " + YAHOO.lang.dump(o));
    }

 });

// Calculate the dependency and insert the required scripts and css resources
// into the document
loader.insert();
Darren Kopp
  • 76,581
  • 9
  • 79
  • 93
  • you can take a look at the link: [answer](https://stackoverflow.com/questions/40808425/how-load-and-use-dynamically-javascript/45489130#45489130) – asmmahmud Aug 03 '17 at 15:59
-1

I have tweaked some of the above post with working example. Here we can give css and js in same array also.

$(document).ready(function(){

if (Array.prototype.contains === undefined) {
Array.prototype.contains = function (obj) {
    var i = this.length;
    while (i--) { if (this[i] === obj) return true; }
    return false;
};
};

/* define object that will wrap our logic */
var jsScriptCssLoader = {

jsExpr : new RegExp( "js$", "i" ),
cssExpr : new RegExp( "css$", "i" ),
loadedFiles: [],

loadFile: function (cssJsFileArray) {
    var self = this;
    // remove duplicates with in array
    cssJsFileArray.filter((item,index)=>cssJsFileArray.indexOf(item)==index)
    var loadedFileArray = this.loadedFiles;
    $.each(cssJsFileArray, function( index, url ) {
            // if multiple arrays are loaded the check the uniqueness
            if (loadedFileArray.contains(url)) return;
            if( self.jsExpr.test( url ) ){
                $.get(url, function(data) {
                    self.addScript(data);
                });

            }else if( self.cssExpr.test( url ) ){
                $.get(url, function(data) {
                    self.addCss(data);
                });
            }

            self.loadedFiles.push(url);
    });

    // don't load twice accross different arrays

},
addScript: function (code) {
    var oNew = document.createElement("script");
    oNew.type = "text/javascript";
    oNew.textContent = code;
    document.getElementsByTagName("head")[0].appendChild(oNew);
},
addCss: function (code) {
    var oNew = document.createElement("style");
    oNew.textContent = code;
    document.getElementsByTagName("head")[0].appendChild(oNew);
}

};


//jsScriptCssLoader.loadFile(["css/1.css","css/2.css","css/3.css"]);
jsScriptCssLoader.loadFile(["js/common/1.js","js/2.js","js/common/file/fileReader.js"]);
});
VG P
  • 337
  • 3
  • 9