139

I'm trying to load a set of scripts in order, but the onload event isn't firing for me.

    var scripts = [
        '//cdnjs.cloudflare.com/ajax/libs/less.js/1.3.3/less.min.js',
        '//cdnjs.cloudflare.com/ajax/libs/handlebars.js/1.0.0-rc.3/handlebars.min.js',
        MK.host+'/templates/templates.js'
    ];

    function loadScripts(scripts){
        var script = scripts.shift();
        var el = document.createElement('script');
        el.src = script;
        el.onload = function(script){
            console.log(script + ' loaded!');
            if (scripts.length) {
                loadScripts(scripts);
            }
            else {
                console.log('run app');
                MK.init();
            }
        };

        $body.append(el);
    }

    loadScripts(scripts);

I guess native events like el.onload don't fire when jQuery is used to append the element to the DOM. If I use native document.body.appendChild(el) then it fires as expected.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
chovy
  • 72,281
  • 52
  • 227
  • 295
  • visit:thi link:http://stackoverflow.com/questions/4845762/onload-handler-for-script-tag-in-internet-explorer This is use fuull – Jitesh Apr 26 '13 at 07:26

3 Answers3

192

You should set the src attribute after the onload event, f.ex:

el.onload = function() { //...
el.src = script;

You should also append the script to the DOM before attaching the onload event:

$body.append(el);
el.onload = function() { //...
el.src = script;

Remember that you need to check readystate for IE support. If you are using jQuery, you can also try the getScript() method: http://api.jquery.com/jQuery.getScript/

David Hellsing
  • 106,495
  • 44
  • 176
  • 212
  • 14
    that actually doesn't fix it. But if i just use `document.body.appendChild(el)` instead of $('body').append(el); then the onload event fires as expected. – chovy Apr 26 '13 at 07:37
  • I’m not sure how to "fix" your code, but best practice is to append the script to the ``, attach the `onload` handler and then the `src` attribute. In that order. – David Hellsing Apr 26 '13 at 07:40
  • i think jQuery is not firing the native onload. (see edit in question). – chovy Apr 26 '13 at 07:42
  • @chovy I think you think wrong. jQuery doesn’t disable native events. It’s the ordering that is important, as I already mentioned. See this example: http://jsbin.com/ucegij/1/edit – David Hellsing Apr 26 '13 at 08:06
  • 47
    can you help me understand why the ordering is important? – chovy Apr 26 '13 at 08:19
  • 15
    @David Why not append the element last, as recommended in places like http://www.html5rocks.com/en/tutorials/speed/script-loading/ ? That should make the order you add the event listener and set the `src` in irrelevant. – Stuart P. Bentley Feb 25 '15 at 07:58
  • 9
    I'm curious: Why is it important to attach an element to the DOM before setting the `onload` and `src` attributes? – Jake Wilson Feb 21 '17 at 00:12
  • 1
    Its not important. Scripts start loading when appended to the document. Images or other medias will start loading as soon as the src attribute is added. So for scripts the order won't make a difference. – Teemoh Jul 02 '17 at 10:35
  • Still helped me, almost exactly 6 years after original answer. Nice! – Nathan Apr 24 '19 at 11:12
  • The order does not matter –  Oct 11 '19 at 17:26
  • 13
    If the script is cached, then as soon as you add the src the item is loaded and onload does not fire. Adding the onload before the src will ensure that onload fires for cached scripts. – JonShipman Nov 25 '19 at 17:38
  • 1
    Remember that onLoad not triggered if the script already in DOM. So you should also previously check the sources of existing scripts and their readyState (to make sure that they are fully loaded in current moment). – Aleksey Kuznetsov Dec 20 '19 at 05:12
  • Mozilla documentation: https://developer.mozilla.org/de/docs/Web/API/HTMLScriptElement#Dynamically_importing_scripts – Abdull Jul 28 '20 at 14:49
13

I faced a similar problem, trying to test if jQuery is already present on a page, and if not force it's load, and then execute a function. I tried with @David Hellsing workaround, but with no chance for my needs. In fact, the onload instruction was immediately evaluated, and then the $ usage inside this function was not yet possible (yes, the huggly "$ is not a function." ^^).

So, I referred to this article : https://developer.mozilla.org/fr/docs/Web/Events/load and attached a event listener to my script object.

var script = document.createElement('script');
script.type = "text/javascript";
script.addEventListener("load", function(event) {
    console.log("script loaded :)");
    onjqloaded(); // in fact, yourstuffscript() function
});
script.src = "https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js";
document.getElementsByTagName('head')[0].appendChild(script);

For my needs, it works fine now. Hope this can help others :)

Philippe
  • 1,134
  • 12
  • 22
0

The working way to load scripts in order (ie. wait until one is loaded, then load another one, etc...)

The following script is added to html page:

<script type="text/javascript">

    document.addEventListener("DOMContentLoaded", (event) => {

        function fn(src) {
            return new Promise(resolve => { // promise is initiated
                const script = document.createElement("script"); // creates <script> tag
                // script.async = true is not required since Promise is used
                script.onload = resolve; // promise will resolve on load event
                script.setAttribute("src", src); // will set srs="link to your source file here"
                document.getElementsByTagName('body')[0].appendChild(script); // append the created <script> tag to <body>
            });
        }

        const url = [ // order of script tags
            "/script1.js", 
            "/script2.js",
            "/script3.js"
        ];

        Promise.resolve(fn(url[0])) // wait until url[0] is resolved, then
            .then(() => fn(url[1])) // call and wait until url[1] is resolved, then
            .then(() => fn(url[2])) // call url[2]

    });

</script>

Important to return result in each ".then" using "() =>" This is called Chaining:

Always return results, otherwise callbacks won't catch the result of a previous promise (with arrow functions, () => x is short for () => { return x; })

Result on loaded html page:

<body>
    <!-- your html -->
    <script src="/script1.js"></script>
    <script src="/script2.js"></script>
    <script src="/script3.js"></script>
</body>

chrome developer tools

Andrew Elans
  • 11
  • 1
  • 3