虚度人生

导航

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();
 */

posted on 2010-05-12 16:33  nozer0  阅读(428)  评论(0编辑  收藏  举报