模拟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 相关文章推荐
flash javascript之间的通讯方法小结
Dec 20 Javascript
JS保存、读取、换行、转Json报错处理方法
Jun 14 Javascript
jQuery学习笔记之jQuery动画效果
Sep 09 Javascript
JS获取url链接字符串 location.href
Dec 23 Javascript
jquery查找父元素、子元素(个人经验总结)
Apr 09 Javascript
JavaScript使用pop方法移除数组最后一个元素用法实例
Apr 06 Javascript
基于JavaScript实现百叶窗动画效果不只单纯flas可以实现
Feb 29 Javascript
详解webpack解惑:require的五种用法
Jun 09 Javascript
JS从非数组对象转数组的方法小结
Mar 26 Javascript
JS函数节流和防抖之间的区分和实现详解
Jan 11 Javascript
layui 富文本赋值,取值,取纯文本值的实例
Sep 18 Javascript
Vue $emit()不能触发父组件方法的原因及解决
Jul 28 Javascript
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 empty函数报错解决办法
2014/03/06 PHP
ThinkPHP提交表单时默认自动转义的解决方法
2014/11/25 PHP
YII Framework框架教程之日志用法详解
2016/03/14 PHP
jquery中常用的SET和GET
2009/01/13 Javascript
JavaScript接口实现代码 (Interfaces In JavaScript)
2010/06/11 Javascript
jQuery 源码分析笔记(5) jQuery.support
2011/06/19 Javascript
Node.js中require的工作原理浅析
2014/06/24 Javascript
jquery使用经验小结
2015/05/20 Javascript
innerHTML中标签可以换行的方法汇总
2015/08/14 Javascript
javascript实现uploadify上传格式以及个数限制
2015/11/23 Javascript
详解JavaScript设计模式开发中的桥接模式使用
2016/05/18 Javascript
移动端js图片查看器
2016/11/17 Javascript
微信小程序 配置顶部导航条标题颜色的实现方法
2017/09/20 Javascript
使用JS实现图片轮播的实例(前后首尾相接)
2017/09/21 Javascript
浅谈Vue网络请求之interceptors实际应用
2018/02/28 Javascript
基于 Immutable.js 实现撤销重做功能的实例代码
2018/03/01 Javascript
详解用vue2.x版本+adminLTE开源框架搭建后台应用模版
2019/03/15 Javascript
详解JQuery基础动画操作
2019/04/12 jQuery
Vue的H5页面唤起支付宝支付功能
2019/04/18 Javascript
使用 js 简单的实现 bind、call 、aplly代码实例
2019/09/07 Javascript
vue Treeselect下拉树只能选择第N级元素实现代码
2020/08/31 Javascript
JavaScript缓动动画函数的封装方法
2020/11/25 Javascript
[57:53]DOTA2上海特级锦标赛主赛事日 - 2 败者组第二轮#3OG VS VP
2016/03/03 DOTA
python的几种开发工具介绍
2007/03/07 Python
在Python中使用元类的教程
2015/04/28 Python
探究Python中isalnum()方法的使用
2015/05/18 Python
python3实现163邮箱SMTP发送邮件
2018/05/22 Python
python 将有序数组转换为二叉树的方法
2019/03/26 Python
Python3 执行系统命令并获取实时回显功能
2019/07/09 Python
python使用requests.session模拟登录
2019/08/09 Python
python修改linux中文件(文件夹)的权限属性操作
2020/03/05 Python
帕克纽约:PARKER NY
2018/12/09 全球购物
动物学专业毕业生求职信
2013/10/11 职场文书
小学语文教学随笔
2015/08/14 职场文书
MySQL数据库中varchar类型的数字比较大小的方法
2021/11/17 MySQL
Java中的继承、多态以及封装
2022/04/11 Java/Android