模拟jQuery中的ready方法及实现按需加载css,js实例代码


Posted in Javascript onSeptember 27, 2013

一、ready函数的实现
经常用jQuery类库或其他类库中的ready方法,有时候想想它们到底是怎么实现的,但是看了一下jQuery中的源码,涉及到的模块比较多,(水平有限)代码比较难看懂;自己结合了一些书籍内容,总结一下。
先说一下ready函数的实现思路:
变量ready通过表达式赋值,右侧为一个自执行匿名函数,在这个匿名函数中,首先为各个浏览器的事件绑定处理函数,并为isReady赋值(根据事件异步处理程序来确定),然后返回一个传参闭包,在闭包中,主要判断isReady值来执行操作,如果dom结构准备就绪(isReady === true),执行回调,否则将回调加入到要执行的队列(funs)中,待事件处理程序执行时,循环遍历队列(funs),并依次执行队列中的函数,执行完队列中的函数后,还需要清除队列(funs = null)。

var ready = (function(){
    var isReady = false,
    funs = [];
    function handle (e) {
        if ( isReady ) {
            return;
        }
        if ( e.type === 'readystatechange' && (document.readyState !== 'interactive' && document.readyState !== 'complete') ) {
            return;
        }
        for ( var i = 0; i < funs.length; i++ ) {
            funs[i].call(document);
        }
        isReady = true;
        funs = null;
    }
    if ( document.addEventListener ) {
        document.addEventListener( 'DOMContentLoaded', handle, false );
        document.addEventListener( 'readystatechange', handle, false );
        document.addEventListener( 'load', handle, false );
    }
    else if ( document.attachEvent ) {
        document.attachEvent( 'onreadystatechange', handle );
        document.attachEvent( 'onload', handle );
    }
    return function ready (callback) {
        if ( isReady ) {
            callback.call(document);
        }
        else {
            funs.push(callback);
        }
    };
}());

PS:
该函数代码参照于权威指南书籍,唯一不同的是,多加了一个判断document.readyState !== 'interactive'
if ( e.type === 'readystatechange' && (document.readyState !== 'interactive' && document.readyState !== 'complete') ) {
    return;
}

在各个浏览器中交互和完成状态出现顺序并不能保证一致,这取决于浏览器及页面的内容,多加了这个判断document.readyState !== 'interactive'的话,
意思是不管哪个阶段先出现,代码都能更早的执行。
二、按需加载css,js
参照了jQuery源码,写了一个type函数,返回参数类型。
/**
 *
 * 判断参数类型
 * createTime: 2013/9/18
 *
 */
function type (obj) {
    var classTypes, objectTypes;
    if ( obj == null ) {
        return String(obj);
    }
    classTypes = {};
    objectTypes = ('Boolean Number String Function Array Date RegExp Object Error').split(' ');
    for ( var i = 0, len = objectTypes.length; i < len; i++ ) {
        classTypes[ '[object ' + objectTypes[i] + ']' ] = objectTypes[i].toLowerCase();
    }
    if ( typeof obj === 'object' || typeof obj === 'function' ) {
        var key = Object.prototype.toString.call(obj);
        return classTypes[key];
    }
    return typeof obj;
}

// css按需加载
function loadCss (cssUrl, callback) {
    var elem, bl,
        isExecuted = false; // 防止在ie9中,callback执行两次
    if ( cssUrl == null ) {
        return String(cssUrl);
    }
    elem = document.createElement('link'),
    elem.rel = 'stylesheet';
    if ( type(callback) === 'function' )  {
        bl = true;
    }
    // for ie
    function handle() {
        if ( elem.readyState === 'loaded' || elem.readyState === 'complete' ) {
            if (bl && !isExecuted) {
                callback();
                isExecuted = true;
            }
            elem.onreadystatechange = null;
        }
    }
    elem.onreadystatechange = handle;
    // for 非ie
    if (bl && !isExecuted) {
        elem.onload = callback;
        isExecuted = true;
    }
    elem.href = cssUrl;
    document.getElementsByTagName('head')[0].appendChild(elem);
}
// js按需加载
function loadScript(scriptUrl, callback) {
    var elem, bl,
        isExecuted = false; // 防止在ie9中,callback执行两次
    if (scriptUrl == null) {
        return String(fn);
    }
    elem = document.createElement('script');
    if ( type(callback) === 'function' )  {
        bl = true;
    }
    // for ie
    function handle(){
        var status = elem.readyState;
        if (status === 'loaded' || status === 'complete') {
            if (bl && !isExecuted) {
                callback();
                isExecuted = true;
            }
            elem.onreadystatechange = null;
        }
    }
    elem.onreadystatechange = handle;
    // for 非ie
    if (bl && !isExecuted) {
        elem.onload = callback;
        isExecuted = true;
    }
    elem.src = scriptUrl;
    document.getElementsByTagName('head')[0].appendChild(elem);
}

PS: 在判断link,script元素是否加载完毕,主要依靠load事件;而在ie9以下浏览器中,并没有load事件,ie为它们都添加了一个readystatechange事件,通过判断
元素的readyState状态确定元素是否已经加载完毕;而奇怪的是,在ie9(还可能存在其他浏览器版本)中,元素既有load事件又有readystatechange事件,因此在代码中添加了一个变量isExecuted,如果执行过回调,那么就不再执行,避免回调执行两次。
三、调用方式
loadCss('https://3water.com/apps/tbtx/miiee/css/base.css', function(){
    console.log('css加载完毕');
});
loadScript('https://3water.com/apps/tbtx/miiee/js/jQuery.js', function(){
    console.log('js加载完毕');
});
ready(function(){
    console.log('dom is ready!');
});
Javascript 相关文章推荐
纯JavaScript实现获取onclick、onchange等事件的值
Dec 29 Javascript
jQuery性能优化技巧分析
Feb 20 Javascript
AngularJs页面筛选标签小功能
Aug 01 Javascript
Bootstrap导航条的使用和理解3
Dec 14 Javascript
javascript实现文字无缝滚动
Dec 27 Javascript
BootStrap select2 动态改变值的方法
Feb 10 Javascript
使用use注册Vue全局组件和全局指令的方法
Mar 08 Javascript
JS返回页面时自动回滚到历史浏览位置
Sep 26 Javascript
vue-router之实现导航切换过渡动画效果
Oct 31 Javascript
javascript用defineProperty实现简单的双向绑定方法
Apr 03 Javascript
JS实现炫酷雪花飘落效果
Aug 19 Javascript
jQuery实现容器间的元素拖拽功能
Dec 01 jQuery
jquery ready函数、css函数及text()使用示例
Sep 27 #Javascript
javascript full screen 全屏显示页面元素的方法
Sep 27 #Javascript
实现动画效果核心方式的js代码
Sep 27 #Javascript
javascript中call和apply方法浅谈
Sep 27 #Javascript
文本框回车提交与禁止提交示例
Sep 27 #Javascript
JQuery事件e参数的方法preventDefault()取消默认行为
Sep 26 #Javascript
原生js实现改变随意改变div属性style的名称和值的结果
Sep 26 #Javascript
You might like
PHP链接MySQL的常用扩展函数
2014/10/23 PHP
深入理解PHP原理之执行周期分析
2016/06/01 PHP
newxtree.js代码
2007/03/13 Javascript
javascript判断单选框或复选框是否选中方法集锦
2007/04/04 Javascript
jquery 图片轮换效果
2010/07/29 Javascript
JS中window.open全屏命令解析及使用示例
2013/12/11 Javascript
jQuery 1.9移除了$.browser可以使用$.support来替代
2014/09/03 Javascript
js获取时间并实现字符串和时间戳之间的转换
2015/01/05 Javascript
Backbone.js 0.9.2 源码注释中文翻译版
2015/06/25 Javascript
举例讲解JavaScript中关于对象操作的相关知识
2015/11/16 Javascript
BootStrap Fileinput初始化时的一些参数
2016/12/30 Javascript
微信小程序 devtool隐藏的秘密
2017/01/21 Javascript
jQuery插件echarts去掉垂直网格线用法示例
2017/03/03 Javascript
HTML的select控件美化
2017/03/27 Javascript
vue组件间通信子与父详解(二)
2017/11/07 Javascript
nodejs超出最大的调用栈错误问题
2017/12/27 NodeJs
jQuery实现table表格信息的展开和缩小功能示例
2018/07/21 jQuery
vue项目中使用Hbuilder打包app 设置沉浸式状态栏的方法
2018/10/22 Javascript
js实现移动端吸顶效果
2020/01/08 Javascript
Vue中点击active并第一个默认选中功能的实现
2020/02/24 Javascript
Python 基于Twisted框架的文件夹网络传输源码
2016/08/28 Python
Django2.1集成xadmin管理后台所遇到的错误集锦(填坑)
2018/12/20 Python
Python 用matplotlib画以时间日期为x轴的图像
2019/08/06 Python
详解Django将秒转换为xx天xx时xx分
2019/09/27 Python
Python matplotlib画图时图例说明(legend)放到图像外侧详解
2020/05/16 Python
Django微信小程序后台开发教程的实现
2020/06/03 Python
浅谈sklearn中predict与predict_proba区别
2020/06/28 Python
美国最大的宠物用品零售商:PetSmart
2016/11/14 全球购物
美国奢侈品在线团购网站:Gilt City
2017/11/16 全球购物
大学生农村教师实习自我鉴定
2013/09/21 职场文书
电子商务助理求职自荐信
2014/04/10 职场文书
服务口号大全
2014/06/11 职场文书
公益广告标语
2014/06/19 职场文书
安全保证书怎么写
2015/02/28 职场文书
python代码实现扫码关注公众号登录的实战
2021/11/01 Python
HTML中实现音乐或视频自动播放案例详解
2022/05/30 HTML / CSS