Posted By

harikaram on 11/03/10


Tagged

ajax js DOM jquery injection events external load ready


Versions (?)

Who likes this?

2 people have marked this snippet as a favorite

Tyster
wirenaught


Load external scripts FIRST for injected DOM HTML content


 / Published in: jQuery
 

When injecting via jQuery HTML content which contains script tags referencing external scripts, which are then referenced via scripting elsewhere in the injected HTML, some browsers (Chrome) will not wait for the external scripts to load causing the internal scripts to throw an error. This code parses the external script tags, loads them seperately and then calls the callback function passing the given HTML minus the external script tags for you to inject.

UPDATE: I've made this considerably more complex after some IE problems (really!). Hope it helps someone out...

  1. /**
  2.  * Parse the external scripts and append them to the document seperately.
  3.  *
  4.  * Ensures external libs are fully loaded prior to any script on the supplied html
  5.  * The callback is give the html minus the previously injected external script tags.
  6.  *
  7.  * @param html Source HTML content or jquery object
  8.  * @param callback Function to call passing the modifed jquery HTML reference
  9.  */
  10. loadContentScripts: function ( html, callback )
  11. {
  12. // Grab the external script tags and strip from the html
  13. var workingHtml = $(html);
  14. var scripts = workingHtml.filter( 'script[src$=".js"]' ).clone();
  15. var v = this;
  16.  
  17. // If no scripts then just callback
  18. if ( scripts.length == 0 ){
  19. callback( html );
  20. return this;
  21. }
  22.  
  23. // Otherwise strip the scripts from the HTML
  24. html = workingHtml.not( 'script[src$=".js"]' );
  25.  
  26. // Gather the script names for later as .data doesnt seem to work well
  27. var scriptLoadMap = new Array();
  28. scripts.each( function ( i, ele ) {
  29. scriptLoadMap[ $(ele).attr( 'src' ) ] = false;
  30. });
  31.  
  32. // Add them seperately and set the callback
  33. scripts.appendTo( 'body' );
  34.  
  35. // Handler to check whether all scripts have been loaded
  36. var done = false;
  37. var handler = function ( e ){
  38. var loadedSrc = $( e.currentTarget ).attr( 'src' );
  39.  
  40. if ( typeof( scriptLoadMap[ loadedSrc ] ) == 'undefined' )
  41. return;
  42.  
  43. // Set this one to true
  44. scriptLoadMap[ loadedSrc ] = true;
  45.  
  46. // Check whether they've all loaded
  47. var allLoaded = true;
  48. scripts.each( function ( i, ele ) {
  49. src = $(ele).attr( 'src' );
  50. allLoaded = allLoaded && scriptLoadMap[ src ];
  51. })
  52.  
  53. // Call the CB if all are loaded
  54. if ( allLoaded && !done ){
  55. callback( html );
  56. done = true; //only call once
  57. }
  58. };
  59.  
  60. // IE compat event checking for a script's load
  61. // This grabs all the pages scripts but thanks to our handler thats ok
  62. $('script[src$=".js"]').load( handler ).each( function(){
  63. var thisSrc = $( this ).attr( 'src' );
  64.  
  65. // Don't bother unless its in out list
  66. if ( typeof( scriptLoadMap[ thisSrc ] ) == 'undefined' )
  67. return;
  68.  
  69. // In case already loaded (like by cache), trigger manually
  70. if ( this.complete )
  71. $( this ).trigger( 'load' );
  72.  
  73. // Special case for IE
  74. else if ( $.browser.msie ){
  75. this.onreadystatechange = function () {
  76. if ( this.readyState == "loaded" || this.readyState == 'complete' ) // ie8 = loaded. 'complete' just in case...
  77. $( this ).load();
  78. };
  79. }
  80.  
  81. // HKS Hack
  82. // Set a timeout period before we force the load, in case browser is acting up (eg. IE)
  83. window.setTimeout(
  84. $.proxy( function ()
  85. {
  86. if ( done ) return; // see above
  87.  
  88. // Tell GA
  89. v._tracker.trackEvent( 'Errors', 'Script Timeout', thisSrc );
  90.  
  91. if ( CONFIG.DEBUG )
  92. console.log( 'Script Load has timed out (' + CONFIG.SCRIPT_LOAD_TIMEOUT+'ms) for file "' + $(this).attr('src') + '". Triggering load manually...' );
  93.  
  94. if ( $.browser.msie )
  95. this.onreadystatechange = null;
  96.  
  97. $( this ).load(); // triggers event
  98. }, this ),
  99. CONFIG.SCRIPT_LOAD_TIMEOUT
  100. );
  101. });
  102.  
  103. return this;
  104. }
  105.  
  106. // USAGE:
  107. var CONFIG = { SCRIPT_LOAD_TIMEOUT: 5000, DEBUG: 0 }
  108. var html = 'Some html containing external scripts <script src="..."';
  109. var container = $( '#content' );
  110. var newContent = $( '<DIV>'+html+'</DIV>' );
  111.  
  112. loadContentScripts( newContent, function ( resultHtml ){
  113. container.append( resultHtml );
  114. } );

Report this snippet  

Comments

RSS Icon Subscribe to comments
Posted By: harikaram on February 26, 2011

My latest version of this is considerably more complex...have a look for inspiration:

_loadContentScripts: function ( html, callback ) { // Grab the external script tags and strip from the html var workingHtml = $(html); var scripts = workingHtml.filter( 'script[src$=".js"]' ).clone(); var v = this;

    // If no scripts then just callback
    if ( scripts.length == 0 ){
        callback( html );
        return this;
    }

    // Otherwise strip the scripts from the HTML
    html = workingHtml.not( 'script[src$=".js"]' );

    // Gather the script names for later as .data doesnt seem to work well
    var scriptLoadMap = new Array();
    scripts.each( function ( i, ele ) {
        scriptLoadMap[ $(ele).attr( 'src' ) ] = false;
    });

    // Add them seperately and set the callback
    scripts.appendTo( 'body' );

    // Handler to check whether all scripts have been loaded
    var done = false;
    var handler = function ( e ){   
        var loadedSrc = $( e.currentTarget ).attr( 'src' );

        if ( typeof( scriptLoadMap[ loadedSrc ] ) == 'undefined' )
            return;

        // Set this one to true
        scriptLoadMap[ loadedSrc ] = true;

        // Check whether they've all loaded
        var allLoaded = true;
        scripts.each( function ( i, ele ) {
            src = $(ele).attr( 'src' );
            allLoaded = allLoaded && scriptLoadMap[ src ];
        })

        // Call the CB if all are loaded
        if ( allLoaded && !done ){
            callback( html );
            done = true;    //only call once
        }
    };

    // IE compat event checking for a script's load
    // This grabs all the pages scripts but thanks to our handler thats ok
    $('script[src$=".js"]').load( handler ).each( function(){
            var thisSrc = $( this ).attr( 'src' );

            // Don't bother unless its in out list
            if ( typeof( scriptLoadMap[ thisSrc ] ) == 'undefined' )
                return;

            // In case already loaded (like by cache), trigger manually
            if ( this.complete )    
                $( this ).trigger( 'load' );

            // Special case for IE
            else if ( $.browser.msie ){
                this.onreadystatechange = function () {
                    if ( this.readyState == "loaded" || this.readyState == 'complete' ) // ie8 = loaded.  'complete' just in case...
                        $( this ).load(); 
                };
            }

            // HKS Hack
            // Set a timeout period before we force the load, in case browser is acting up (eg. IE)
            window.setTimeout( 
                $.proxy( function ()
                {
                    if ( done ) return;     // see above

                    // Tell GA
                    v._tracker.trackEvent( 'Errors', 'Script Timeout', thisSrc );

                    if ( EcourseOSConfig.DEBUG )
                        console.log( 'Script Load has timed out (' + EcourseOSConfig.SCRIPT_LOAD_TIMEOUT+'ms) for file "' + $(this).attr('src') + '".  Triggering load manually...' );

                    if ( $.browser.msie )
                        this.onreadystatechange = null;

                    $( this ).load();       // triggers event
                }, this ), 
                EcourseOSConfig.SCRIPT_LOAD_TIMEOUT
            );
    });

    return this;
},

You need to login to post a comment.