Reputation: 546045
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) {
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?
Upvotes: 6
Views: 6420
Reputation: 945
This is the function I use to load external script and style files dynamically into my page, considering not to load an script twice and detecting both '.css' and '.js' extentions.
function lazyLoad(src) {
head = document.getElementsByTagName('head')[0];
if (head.innerHTML.search(src) == -1) {
if (src.search('.js') > -1) {
var node = document.createElement('script');
node.src = src;
node.async = false;
node.setAttribute('charset', 'utf-8');
head.appendChild(node);
} else if (src.search('.css') > -1) {
var node = document.createElement('link');
node.href = src;
node.setAttribute('rel', 'stylesheet');
node.setAttribute('charset', 'utf-8');
head.appendChild(node);
}
}
}
Upvotes: -1
Reputation: 116980
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.
Upvotes: 5
Reputation: 7147
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;
Upvotes: 0
Reputation: 106027
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.
Upvotes: 3