admin管理员组文章数量:1291061
I'm looking for a way to conditionally load and keep the execution order of some javascript files (external and internal) without any library dependency. Basically, what I want to do is load them up only if the browser supports localStorage.
Here's basically my shell:
if (window.localStorage) {
//load up JS only if it's needed
var body = document.getElementsByTagName('body')[0],
js1 = document.createElement('script'),
js2 = document.createElement('script'),
js3 = document.createElement('script'),
js4 = document.createElement('script'),
js5 = document.createElement('script');
js1.src = '.4.2/jquery.min.js';
js2.src = '';
js3.src = 'my_file1.js';
js4.src = 'my_file2.js';
js5.src = 'my_file3.js';
body.appendChild(js1);
body.appendChild(js2);
body.appendChild(js3);
body.appendChild(js4);
body.appendChild(js5);
} else {
//no localStorage support, display messaging
}
I've tried dynamically adding script nodes via createElement/body.appendChild but those don't seem to work.
Is there an easy way to achieve this? Right now everything works, but IE6 and IE7 folks download script they aren't even executing, which is what I want to fix.
I'm looking for a way to conditionally load and keep the execution order of some javascript files (external and internal) without any library dependency. Basically, what I want to do is load them up only if the browser supports localStorage.
Here's basically my shell:
if (window.localStorage) {
//load up JS only if it's needed
var body = document.getElementsByTagName('body')[0],
js1 = document.createElement('script'),
js2 = document.createElement('script'),
js3 = document.createElement('script'),
js4 = document.createElement('script'),
js5 = document.createElement('script');
js1.src = 'http://ajax.googleapis./ajax/libs/jquery/1.4.2/jquery.min.js';
js2.src = 'http://www.google./jsapi';
js3.src = 'my_file1.js';
js4.src = 'my_file2.js';
js5.src = 'my_file3.js';
body.appendChild(js1);
body.appendChild(js2);
body.appendChild(js3);
body.appendChild(js4);
body.appendChild(js5);
} else {
//no localStorage support, display messaging
}
I've tried dynamically adding script nodes via createElement/body.appendChild but those don't seem to work.
Is there an easy way to achieve this? Right now everything works, but IE6 and IE7 folks download script they aren't even executing, which is what I want to fix.
Share Improve this question edited Oct 19, 2010 at 23:23 magenta asked Oct 19, 2010 at 22:57 magentamagenta 4011 gold badge5 silver badges6 bronze badges 2- @Crowder Gulp, I didn't know that. I hope the IE users will forgive me. :) – Šime Vidas Commented Oct 19, 2010 at 23:23
- @Crowder Well, I killed the joke, I think it would be lame to repost a milder version. :) – Šime Vidas Commented Oct 19, 2010 at 23:47
3 Answers
Reset to default 6Adding script
nodes should work just fine. Because those scripts will execute asynchronously to the code adding them, you'll need to give them a callback to call to do the next thing in order. E.g.:
if (window.localStorage) {
// Load the local storage stuff; once loaded, it'll call
// `doTheNextThing`
var script = document.createElement('script');
script.type = "text/javascript";
script.src = /* ... the URL of the script ... */;
document.body.appendChild(script); // Or append it to `head`, doesn't matter
// and `document.body` is convenient
}
else {
// Skip loading it
setTimeout(doTheNextThing, 10);
}
function doTheNextThing() {
// ...
}
...where the dynamic script you're loading for the localStorage
stuff call doTheNextThing
after it loads — so in the case where there's localStorage
, the dynamically-loaded script calls doTheNextThing
but in the case where there isn't, the code above does. Note that I made the call from the code above asynchronous (via setTimeout
) on purpose: Making it always asynchronous regardless of how it gets called reduces your odds of missing bugs (e.g., adding something that relies on it being called synchronously and then forgetting to test that minor change on IE).
Update: The above assumes you're in control of the script you're loading, but you've clarified that you're not. In that case, what you need to do is load the scripts one at a time and poll for the feature that they provide (usually a property on the window
object, like window.jQuery
), something like this (untested):
// Load the script designated by `src`, poll for the appearance
// of the symbol `name` on the `window` object. When it shows
// up, call `callback`. Timeout if the timeout is reached.
function loadAndWait(src, name, timeout, callback) {
var stop, script;
// Do nothing if the symbol is already defined
if (window[name]) {
setTimeout(function() {
callback("preexisting");
}, 10);
}
else {
// Load the script
script = document.createElement('script');
script.type = "text/javascript";
script.src = src;
document.body.appendChild(script);
// Remember when we should stop
stop = new Date().getTime() + timeout;
// Start polling, long-ish initial interval
setTimeout(poll, 150);
}
function poll() {
if (window[name]) {
// Got it
callback("loaded");
}
else if (new Date().getTime() > stop) {
// Time out
callback("timeout");
}
else {
// Keep waiting, shorter interval if desired
setTimeout(poll, 75);
}
}
}
...which you'd use like this for the jQuery load:
loadAndWait(
"http://ajax.googleapis./ajax/libs/jquery/1.4.2/jquery.min.js",
"jQuery",
10000, // ten seconds or whatever
function(result) {
// ...do the next one if result !== "timeout"
}
);
You can either nest calls to loadAndWait
in each of the previous calls' callbacks, or use an array and counter:
loadThese(
[
{ src: "http://ajax.googleapis./ajax/libs/jquery/1.4.2/jquery.min.js",
symbol: "jQuery"
},
{
src: "http://the-next-one",
symbol: "nextSymbol"
}
],
doTheNextThing
);
function loadThese(scripts, callback) {
var index = 0;
run("okay");
function run(result) {
var entry;
if (result === "timeout") {
callback(result);
}
else if (index < scripts.length) {
entry = scripts[index++];
loadAndWait(entry.src, entry.symbol, 10000, run);
}
else {
callback("loaded");
}
}
}
There, loadThese
sets up a loop using run
to load each script in turn.
All of the above is pletely off-the-cuff and can probably be tightened and bullet-proofed, but you get the idea.
Off-topic, but my question is: Is there really so much code that it's a problem for the browsers that can't use it to load it? Barring the files getting a lot bigger, you'll actually slow down your site for users with advanced browsers without gaining much of anything on the others. Below a certain size, the overhead of connecting to the server to retrieve the script is as big a factor as transferring it. Is the extra stuff 50k of code? I'd do some benchmarking to test whether it's really necessary... Perhaps it is (perhaps you already have!), but it's worth just mentioning...
Off-topic update: In your updated question, you list five separate scripts you'd be downloading if localStorage
is supported. Even assuming you're getting all five from various CDNs, that's a lot of individual script requests (whether done in the usual way or as above), each of which has to be processed one at a time. That's a page load performance issue waiting to happen. Despite (possibly) losing the benefits of CDNs and existing caching, you might look at grabbing all of those scripts, bining them, and hosting your bined version in a single file. See "Minimize HTTP Requests" in the YUI performance "rules" (I prefer the term "guideline", but whatever). It would also simplify your dynamic loading.
You can use the bination of onload and closure function. Something like the following:
function loadScripts(index) {
return function () {
var e = document.createElement('script');
e.src = scripts[index];
document.body.appendChild(e);
if (index + 1 < scripts.length) {
e.onload = loadScripts(index + 1)
}
};
}
And invoke it like this:
loadScripts(0)();
I've e to use this code. Both main functions (addEvent and load_javascript) are found on the web.
I wasn't trying to reduce download size, though: this is the only way I could load resources. So, maybe the idea proposed by Šime Vidas makes sense for you.
addEvent = function(elm, evType, fn, useCapture) {
//Credit: Function written by Scott Andrews
//(slightly modified)
var ret = 0;
if (elm.addEventListener) {
ret = elm.addEventListener(evType, fn, useCapture);
} else if (elm.attachEvent) {
ret = elm.attachEvent('on' + evType, fn);
} else {
elm['on' + evType] = fn;
}
return ret;
};
var left_to_load = 0;
function init() {
--left_to_load;
if (left_to_load > 0) {
return;
}
// all scripts are loaded now
// proceed with your logic
}
// load js file and call function when done
function load_javascript(src, callback) {
var a = document.createElement('script');
a.type = 'text/javascript';
a.src = src;
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(a, s);
++left_to_load;
addEvent(a, 'load', callback, false);
}
load_javascript('url1', init);
load_javascript('url2', init);
...
版权声明:本文标题:lazy loading - conditionally load javascript (external and internal) and keep execution order - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1741524886a2383408.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论