preload&lazy load
最近需要用到预加载和延迟加载的东东,就参考写了一个。 支持跨页面,支持超时设置与依赖设置。
(function($) { (function($) { $.preload = function(data, cfg) { return new Loader(data, cfg); }; var maps = {}, on = $.event.add, un = $.event.remove, head = document.getElementsByTagName('head')[0], body = document.body, bs = $.browser, ie = bs.msie, webkit = bs.webkit, gecko = bs.mozilla, space = 1000, ajax = $.ajax, loaders = $.preload.loaders = { 'js' : function(url, callback, timeout, defer) { var s, timer; if (defer) { if (ie) { return loaders.img(url, callback, timeout); } else { s = document.createElement('object'); s.data = url; s.width = s.height = 0; } } else { s = document.createElement('script'); s.setAttribute('type', 'text/javascript'); s.setAttribute('src', url); } function f() { if (timer) clearTimeout(timer); s.onreadystatechange = s.onload = s.onerror = null; callback(url, false); } if (ie) { s.onreadystatechange = function() { if (this.readyState === 'loaded' || this.readyState === 'complete') { if (timer) clearTimeout(timer); s.onreadystatechange = null; callback(url, true); } }; } else { s.onload = function() { if (timer) clearTimeout(timer); s.onload = s.onerror = null; callback(url, true); }; s.onerror = f; } timer = setTimeout(f, timeout); body.appendChild(s); }, 'css' : function(url, callback, timeout, defer) { if (defer) { return loaders.js(url, callback, timeout, defer); } var s = document.createElement('link'), timer; s.setAttribute('rel', 'stylesheet'); s.setAttribute('type', 'text/css'); s.setAttribute('href', url); function f() { if (timer) clearTimeout(timer); s.onreadystatechange = s.onload = s.onerror = null; callback(url, false); } if (ie) { s.onreadystatechange = function() { if (this.readyState === 'loaded' || this.readyState === 'complete') { if (timer) clearTimeout(timer); s.onreadystatechange = null; callback(url, true); } }; timer = setTimeout(f, timeout); } else if (webkit || gecko) { timer = new Date(); function f() { if (('sheet' in s) && ('cssRules' in s.sheet)) { try { callback(url, !!s.sheet.cssRules[0]); } catch (e) { setTimeout(f, space); } } else if (new Date() - timer > timeout) { callback(url, false); } else { setTimeout(f, space); } } setTimeout(f, space * 2); } else { s.onload = function() { if (timer) clearTimeout(timer); s.onload = s.onerror = null; callback(url, true); }; s.onerror = f; timer = setTimeout(f, timeout); } head.appendChild(s); }, 'img' : function(url, callback, timeout) { var s = new Image(), timer; function f() { if (timer) clearTimeout(timer); s.onload = s.onerror = null; callback(url, false); } s.onload = function() { if (timer) clearTimeout(timer); s.onload = s.onerror = null; callback(url, true); }; s.onerror = f; timer = setTimeout(f, timeout); s.src = url; }, 'ajax' : function(url, callback, cfg) { cfg = cfg || {}; cfg.url = url; cfg.success = function(data) { callback(url, true, data); }; cfg.error = function() { callback(url, false); }; ajax(cfg); } }; function Loader(data, cfg) { var self = this, cur = -1, items = [], pendings = [], done, i = 0, l = data.length, j, m, s, t, c, d, tt, item, doing = 0, load; cfg = cfg || {}; for (; i < l; ++i) { item = data[i]; if (typeof item === 'string') { s = item.substr(item.lastIndexOf('.') + 1); items.push(maps[item] = { type : loaders[s] ? s : 'img', url : item }); } else if (item.urls) { for (j = 0, s = item.type, t = item.require, c = item.callback, d = item.defer, tt = item.timeout, item = item.urls, m = item.length; j < m; ++j) { s = s || item[j].substr(item[j].lastIndexOf('.') + 1); items.push(maps[item[j]] = { type : loaders[s] ? s : 'img', url : item[j], require : t, callback : c, defer : d, timeout : tt }); } } else { if (!item.type) { s = item.url.substr(item.url.lastIndexOf('.') + 1); item.type = loaders[s] ? s : 'img'; } items.push(maps[item.url] = item); } } this.success = this.fail = this.progress = 0; if (cfg.onFinish) this.onFinish = cfg.onFinish; timeout = cfg.timeout || 2000; function callback(url, flag, data) { if (flag) { ++self.success; } else { ++self.fail; } self.progress = (self.success + self.fail) / items.length; console.info(url); console.warn(flag); item = maps[url]; item.success = flag; if (self.progress === 1) { self.stop(); } if (item.parent && !item.defer && !cfg.defer) { $(item.parent)[0].innerHTML = data || ''; } if (item.callback) { item.callback(data); } item.done = true; --doing; } function runnable(item, pend) { var it; if (typeof item.require === 'string') { if (item.done) return false; if (!item.require) return true; it = maps[item.require]; if (!it || it.done) { if (pend) pendings.shift(); if (it && it.success) { return true; } else { callback(item.url, false); } } else if (!pend) { pendings.push(item); } } else { for (it = item.length; it--;) { if (!runnable(item[it], pend)) return false; } return true; } } function run() { var item = pendings[0]; if (!item || !runnable(item, true)) { while (item = items[++cur]) { if (runnable(item)) { break; } } } if (item) { var fn = loaders[item.type || 'img']; if (fn) { ++doing; if (item.type === 'ajax') { if (item.cfg && !item.cfg.timeout) item.cfg.timeout = timeout; fn(item.url, callback, item.cfg); } else { fn(item.url, callback, item.timeout || timeout, item.defer === undefined ? cfg.defer : item.defer); } }; if (load) { run(); } else { self.timer = setTimeout(run, space); } } else if (pendings.length) { self.timer = setTimeout(run, space); } } this.start = function(delay) { if (!done) this.timer = setTimeout(run, delay > space ? delay : space); }; this.stop = function() { if (this.timer) { clearTimeout(this.timer); this.timer = null; done = true; if (this.onFinish) { if (!doing) this.onFinish(); else { s = setInterval(function() { if (!doing) { clearInterval(s); self.onFinish(); } }, space); } } } }; this.pause = function() { clearTimeout(this.timer); }; this.resume = function() { this.timer = setTimeout(run, space); }; this.load = function() { clearTimeout(this.timer); load = true; run(); }; } })(jQuery); /** * @example * var loader = $.preload([ // 字符串,采用默认配置 '1.jpg', '1.js', // 对象,自定义配置,如type, require, timeout, defer, callback { type : 'img', url : 'http://foo.com/foo', timeout : 10 }, { url : '3.js', callback : fn, defer : true, require : '1.js' }, // 对象,可用urls指定一组相同配置 { type : 'css', urls : ['4.css', '5.css'] }], { // 加载结束后调用 onFinish : fn, // 加载超时 timeout : 50 }); // 开始预加载 loader.start(); loader.stop(); // 暂停预加载 loader.pause(); loader.resume(); // 实时加载 loader.load(); */