ThiefMaster
ThiefMaster

Reputation: 318638

Why doesn't .on('load', ...) fire the callback for a script tag?

I have the following code which loads an external script and is supposed to execute some code when it's loaded:

$('<script/>', {
    type: 'text/javascript',
    src: 'https://raw.github.com/einars/js-beautify/master/beautify.js'
}).on('load', function() {
    alert('jsb loaded' + typeof js_beautify);
}).appendTo('body');

However, the event never fires - even though the script is properly loaded, as verified with

window.setTimeout(function() {
    alert(typeof js_beautify);
}, 1000);

which alerts function just fine.

Demo: http://jsfiddle.net/ThiefMaster/x2b9x/

Upvotes: 3

Views: 314

Answers (2)

Shen liang
Shen liang

Reputation: 1435

(jQuery:2.0.3) 2 Reasons for the non-triggered load event on the dynamic script tag injection through appendTo():

  • load event isn't a bubble event. The invoked handlers will be only hooked on that particular element only
  • appendTo() isn't really inject the element provided by your code into the DOM as expected

getScript() use DOM function directly

document.head.appendChild( script[ 0 ] );

to inject the script tag element. This ensure the tag element is actually added into the DOM. While appendTo() will try to use Ajax to load the script from the source URL instead. Then the load event is triggered for window object instead of the script object.

The following is the detail testing on the jQuery.appendTo() to learn the reason

                var $script = $("<script>", {
                    src: this.options.url_js,
                    "data-widget": this.widgetFullName,
                    type:"text/javascript"
                }).on(
                    "load error",
                    function( evt ) {
                        script.remove();
                        if ( evt ) {
                            alert( evt.type === "error" ? 404 : 200, evt.type );
                        }
                    }
                ).appendTo("head");
  1. jQuery.on() will register the event handler into the "data_priv" using the element as the key
  2. when event happened, jQuery will use "jQuery.event.dispatch.apply( eventHandle.elem, arguments )" to invoke the handlers.

jQuery.appendTo() is the alias to the "append". The actual code is

   append: function() {
    return this.domManip( arguments, function( elem ) {
        if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
            var target = manipulationTarget( this, elem );
            target.appendChild( elem );
        }
    });
}

Before actually inject the element into the DOM, it will use the domManip() to do some ajax stuff.

  domManip: function( args, callback, allowIntersection ) {

There are 2 key variables in this function, 1 is "value", 2nd is "node". The "value" is the variable "$script". the actual injected element is the "node", which is not the "$script"

  callback.call( this[ i ], node, i ); //inject the element into the DOM  

firefox comparison on the variable node & value

That is why the "load" event handler on the "$script" won't be invoked at all.

But this load event will be triggered twice, 1 is for the loaded element, (here is the node), another one is for the window object. If your handler is hooked on the window object, it will be invoked.

Upvotes: 2

SLaks
SLaks

Reputation: 887867

You should call $.getScript(), which does exactly that, and works correctly.

Upvotes: 2

Related Questions