Waiting for dynamically loaded script

JavascriptJqueryHtmlLoad Order

Javascript Problem Overview


In my page body, I need to insert this code as the result of an AJAX call:

    <p>Loading jQuery</p>
    <script type='text/javascript' src='scripts/jquery/core/jquery-1.4.4.js'></script>
    <p>Using jQuery</p>
    <script type='text/javascript'>
        $.ajax({
            ...
        });
    </script>

I can't use $.load() since the document has already loaded, so the event doesn't fire.

Is this safe? If not, how do I make sure the jquery script has loaded before my custom, generated code is executed.

Javascript Solutions


Solution 1 - Javascript

Add an ID to your script file so you can query it.

<script id="hljs" async src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.0.0/highlight.min.js"></script>

Then add a load listener to it in JavaScript

<script>
  var script = document.querySelector('#hljs');
  script.addEventListener('load', function() {
    hljs.initHighlightingOnLoad(); 
  });
</script>

Solution 2 - Javascript

It is pretty safe. Historically, <script> tags are full blocking, hence the second <script> tag can't get encountered befored the former has finished parsing/excuting. Only problem might be that "modern" browsers tend to load scripts asynchronously and deferred. So to make sure order is correct, use it like this:

<p>Loading jQuery</p>
<script type='text/javascript' async=false defer=false src='scripts/jquery/core/jquery-1.4.4.js'></script>
<p>Using jQuery</p>
<script type='text/javascript'>
    $.ajax({
        ...
    });
</script>

However, it's probably a better idea it use dynamic script tag insertion instead of pushing this as HTML string into the DOM. Would be the same story

var scr  = document.createElement('script'),
    head = document.head || document.getElementsByTagName('head')[0];
    scr.src = 'scripts/jquery/core/jquery-1.4.4.js';
    scr.async = false; // optionally

head.insertBefore(scr, head.firstChild);

Solution 3 - Javascript

const jsScript = document.createElement('script')
jsScript.src =
  'https://coolJavascript.js'

document.body.appendChild(jsScript)

jsScript.addEventListener('load', () => {
  doSomethingNow()
})

Will load after the script is dynamically added

Solution 4 - Javascript

Wait for multiple scripts to load

The following helper loads multiple scripts only once and returns a promise:

async function cirosantilli_load_scripts(script_urls) {
    function load(script_url) {
        return new Promise(function(resolve, reject) {
            if (cirosantilli_load_scripts.loaded.has(script_url)) {
                resolve();
            } else {
                var script = document.createElement('script');
                script.onload = resolve;
                script.src = script_url
                document.head.appendChild(script);
            }
        });
    }
    var promises = [];
    for (const script_url of script_urls) {
        promises.push(load(script_url));
    }
    await Promise.all(promises);
    for (const script_url of script_urls) {
        cirosantilli_load_scripts.loaded.add(script_url);
    }
}
cirosantilli_load_scripts.loaded = new Set();

(async () => {
    await cirosantilli_load_scripts([
        'https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/1.3.8/FileSaver.min.js',
        'https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.8.0/Chart.min.js',
    ]);

    // Now do stuff with those scripts.

})();

GitHub upstream: definition and usage.

Tested in Chromium 75.

Solution 5 - Javascript

There is also new feature in jQuery 1.6. It is called jQuery.holdReady(). It is actually self explanatory; when you call jQuery.holdReady(true), ready event is not fired until you call jQuery.holdReady(false). Setting this to false will not automatically fire a ready event, it just removes the hold.

Here is a non-blocking example of loading a script taken from the documentation:

$.holdReady(true);
$.getScript("myplugin.js", function() {
     $.holdReady(false);
});

See http://api.jquery.com/jQuery.holdReady/ for more information

Solution 6 - Javascript

	new MutationObserver((mutationsList, observer) => {
	    for (var mutation of mutationsList) 
	        if (mutation.type === 'childList') 
	            Array.from (mutation.addedNodes)
					.filter (node => node.tagName === 'SCRIPT')
					.forEach (script => {
                        //Script started loading
						script.addEventListener ('load', () => { 
                             //Script finished loading
						})
					})
	}).observe(document, { attributes: false, childList: true, subtree: true });

Solution 7 - Javascript

In my case the solutions didn't work. I wanted to programmatically click a link after a script has loaded the right click event for the link. Thus I had to work with timeout and loop:

<script>
  var ifrbutton = document.getElementById("id-of-link");
  if(typeof(ifrbutton) != 'undefined' && ifrbutton != null && window.location.hash == "#special-hash") {
    var i = 0;
    // store the interval id to clear in future
    var intr = setInterval(function() {
      if (ifrbutton.onclick!=null) {
        ifrbutton.click();
        clearInterval(intr);
        i = 200;
      }
      if (++i >= 200) clearInterval(intr);
    }, 300)
  }
</script>

Thus the solution could also be to check in intervals for certain functionality that the script brings with it... Set some secure end just in case the script gets never loaded... Works safely for me ;)

Solution 8 - Javascript

this works for me

function loadScript(sources, callBack) {
    var script 		= document.createElement('script');
    script.src 		= sources;
    script.async 	= false; 
	document.body.appendChild(script); 
	
    script.addEventListener('load', () => {
	    if(typeof callBack == "function") callBack(sources);
	});
}

Solution 9 - Javascript

$.ajax and $.load are the same thing. You can use either. If you put $.load in a script tag on the page it will fire when it loads just like $.ajax().

When it comes to waiting for a script to fire before you do something what Tomalak said is kinda true. The exception is asynchronous calls. Javascript will make an AJAX call then continue running the script and when the AJAX call responds it will run the success/fail depending on what you tell it to do.

Now say you want your JS to wait for the return, with a single call it's pretty easy just wrap every thing in the success callback, or you could do it the cool way and use $.deferred. This allows you to make a bunch of ajax calls or one and then run some code only after the finish.

$.when & $.then are probably the best for this situation.

Any way what your are doing is safe.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAaron DigullaView Question on Stackoverflow
Solution 1 - JavascriptRob FoxView Answer on Stackoverflow
Solution 2 - JavascriptjAndyView Answer on Stackoverflow
Solution 3 - JavascriptLuke RobertsonView Answer on Stackoverflow
Solution 4 - JavascriptCiro Santilli Путлер Капут 六四事View Answer on Stackoverflow
Solution 5 - JavascriptUmur KontacıView Answer on Stackoverflow
Solution 6 - Javascriptuser1593165View Answer on Stackoverflow
Solution 7 - JavascriptchakmearView Answer on Stackoverflow
Solution 8 - JavascriptManoj ChavankeView Answer on Stackoverflow
Solution 9 - JavascriptaustinbvView Answer on Stackoverflow