filur
filur

Reputation: 1566

Dynamic script never fires load callback

Here is a simplified version of my problem. Why isn't the loaded callback fired?

My goal is to inject a script file - which I can't reference by src - in to the head tag of the document:

(function(module, scriptContent, loaded) {

    if (!window[module]) {

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

        if (script.readyState) {
            script.onreadystatechange = function() {
                if (script.readyState === 'loaded' || script.readyState === 'complete') {
                    script.onreadystatechange = null;
                    loaded();
                }
            };
        } else {
            script.onload = loaded;
        }

        script.appendChild(document.createTextNode(scriptContent));
        head.appendChild(script);
    } else {
        loaded();
    }
})('foo', 'var foo = {};', function(){
    console.log('loaded');
});

https://jsfiddle.net/on23j3wk/

Upvotes: 0

Views: 39

Answers (1)

Siguza
Siguza

Reputation: 23870

Because inline scripts are run synchronously, and so don't use readyState.

This can be demonstrated by changing your 'var foo = {};' to 'console.log("b")', and adding console.log('a') before, and console.log('c') after head.appendChild(script);:

(function(module, scriptContent, loaded) {

    if (!window[module]) {
    	
        var script = document.createElement('script'),
            head = document.getElementsByTagName('head')[0];
            
        if (script.readyState) {
            script.onreadystatechange = function() {
                if (script.readyState === 'loaded' || script.readyState === 'complete') {
                    script.onreadystatechange = null;
                    loaded();
                }
            };
        } else {
            script.onload = loaded;
        }
        
        script.appendChild(document.createTextNode(scriptContent));
        console.log('a');
        head.appendChild(script);
        console.log('c');
    } else {
        loaded();
    }
})('foo', 'console.log("b")', function(){
	console.log('loaded');
});

Note how they're all logged in alphabetical order.
If the inserted script were loaded asynchronously, the call stack that created it would have to terminate first, which would lead to "a" and "c" being logged before "b".

In other words, you can call loaded() right after head.appendChild(script);.

Upvotes: 1

Related Questions