Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Invoking handler when all scripts finished loading via $.getScript

So let's say we load a bunch of scripts via $.getScript:

$.getScript( 'js/script1.js' );
$.getScript( 'js/script2.js' );
$.getScript( 'js/script3.js' );

Now, I'd like to invoke a handler when all those scripts finished loading. I tried binding a handler for the global ajaxStop event. According to the docs, the ajaxStop global event is triggered if there are no more Ajax requests being processed.

$( document ).ajaxStop( handler );

but it doesn't work (the handler is not invoked).

Live demo: http://jsfiddle.net/etGPc/2/

How can I achieve this?

like image 856
Šime Vidas Avatar asked May 10 '26 16:05

Šime Vidas


2 Answers

I digged a little more into this issue and it's indeed the cross-domain script request that's the caveat. As I posted in the comments, that scenario has been implemented such that it sets the global option to false. This makes jQuery not to fire global ajax events. (No idea why that has been implemented though.)

This can be confirmed with this fiddle (pass means ajaxStop is fired):

  • cross-domain, no script: pass
  • cross domain, script: fail
  • no cross-domain, no script: pass
  • no cross-domain, script: pass

The most straight-forward thing to do is simply adding another prefilter which forces the global option to true:

jQuery.ajaxPrefilter( "script", function() {
    s.global = true;
});

This also makes this failing scenario pass in the fiddle.

like image 124
pimvdb Avatar answered May 13 '26 06:05

pimvdb


you're doing it wrong anyways :)

here is one of the things that can happen: imagine the scripts are cached, then they might be loaded in no time. so, straight after the first call $.getScript( 'js/script1.js' ); the script will be available and $.ajaxStop (might!!!) get called, in the worst case that would happen three times.

to answer your question indirectly i would propose a different solution which avoids this race condition alltogether. you can try it here: http://jsfiddle.net/etGPc/8/

var urls, log, loaded;

// urls to load
urls = [
    'https://raw.github.com/h5bp/html5-boilerplate/master/js/script.js',
    'https://raw.github.com/h5bp/html5-boilerplate/master/js/plugins.js',
    'https://raw.github.com/h5bp/html5-boilerplate/master/js/libs/modernizr-2.0.6.min.js'
];

// urls loaded 
loaded = []; 


log = $( '#log' ); 

$.map( urls, function( url ){
    $.getScript( url, function(){
        // append to loaded urls
        loaded.push( url ); 
        log.append( "loaded " + url + "<br>" ); 

        // all loaded now? 
        if( loaded.length == urls.length ){
            log.append( "<b>all done!</b>" ); 
        }
    } ); 
} ); 

if you haven't seen jQuery.map before: it's not really different from a for-loop :)

another advantage here is that this method doesn't get confused if you have other ajax requests going on at the same time.

p.s. to avoid naming-clashes you can wrap the entire thing in a self-executing function, i.e.

function(){
    var urls, log, loaded; 

    ... all code here ... 
} (); 

Update: Refactored the code a bit...

var urls, loadedUrls, log;

urls = [
    'https://raw.github.com/h5bp/html5-boilerplate/master/js/script.js',
    'https://raw.github.com/h5bp/html5-boilerplate/master/js/plugins.js',
    'https://raw.github.com/h5bp/html5-boilerplate/master/js/libs/modernizr-2.0.6.min.js'
];    

loadedUrls = [];

log = $( '#log' )[0];

urls.forEach(function ( url ) {
    $.getScript( url, function () {
        loadedUrls.push( url );

        $( log ).append( 'loaded ' + url + '<br>' );

        if( loadedUrls.length === urls.length ){
            $( log ).append( '<b>all done!</b>' );
        }
    });
});

Live demo: http://jsfiddle.net/etGPc/10/

like image 31
kritzikratzi Avatar answered May 13 '26 06:05

kritzikratzi