开发者

Dynamically including javascript files only once

I have a javascript function I'm writing which is being used to include an external JS file, but only once. The reason I need such a function is because it is being called when some content is loaded via AJAX and I need to run page-specific code to that content (no, just using .live won't cover it).

Here's my attempt, shortened for brevity:

$.include_once = function(filename) {开发者_C百科
    if ($("script[src='" + filename + "']").length === 0) {
        var $node = $("<script></script>")
            .attr({
                src : filename,
                type : "text/javascript"
            })
        ;
        $(document.body).append($node);
    }
};

This works fine: the function is called, it loads the external file, and that file is being run when loaded. Perfect.

The problem is that it will always re-load that external file: the query I'm using to check for the presence of the script always finds nothing!

When debugging this, I added some lines:

alert($("script").length);     // alerts: 4
$(document.body).append($node);
alert($("script").length);     // alerts: 4

Looking in the dynamic source (the HTML tab of Firebug), I can't find the script tag at all.

I know that I could maintain an array of files that I've previously included, but I was hoping to go with a method such as this, which (if it worked), seems a bit more robust, since not all the JS files are being included in this way.

Can anyone explain the behaviour seen in this second snippet?


jQuery is a bit of a dumb-dumb in this case; it doesn't do at all what you'd expect. When you append($node) jQuery does this:

jQuery.ajax({
  url: $node.src,
  async: false,
  dataType: "script"
})

Woops! For local files (eg on the same domain) jQuery performs a standard XMLHttpRequest for the .js file body, and proceeds to "eval" it by a whole convoluted process of creating a <script> tag (again!) and settings it's contents to your .js file body. This is to simulate eval but in the global context.

For cross-domain files, since it cannot perform the standard XMLHttpRequest due to the same-domain policy, jQuery once again creates a <script> element and inserts it into <head>.

In both the local and cross-domain cases above jQuery finally gets around to doing this:

head.removeChild(script);

And booms your .length check! Bummer.

So on to your problem, don't bother jQuery with this. Just do

document.getElementsByTagName('head')[0]
  .appendChild(
    document.createElement('script')
  )
  .src = filename;

Which will do what you'd expect, particularly wrt querying for it later.


You're trying to solve a problem that has already been solved several times over. Try LazyLoad for example. There are also similar plugins for jQuery.


Instead of setting the source attribute of the script-tag, set the "text" attribute of the script tag. This works in all modern browsers (the application where I use that in practice does not support IE6, so I do not know about this creep...).

In practice it would look like this (you HAVE TO add code to omit double inclusion on yourself - e.g. a simple array of all alread loaded scripts, though thats very application specific, and why should you anyway load code twice? try to omit double-loading code...!):

var script_source_code_string = <some dynamically loaded script source code>;
var $n = $("<script></script>");
$n.get(0).text = script_source_code_string;
$(document.body).append($n);

Or even simpler (without jquery, my code at this stage does not know jquery, it may also be loaded dynamically):

var script_source_code_string = <some dynamically loaded script source code>;
var s = document.createElement('script');
document.getElementsByTagName('head')[0].appendChild(s);
s.text = script_source_code_string;
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜