模拟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 图片上传预览-兼容标准
Jun 01 Javascript
javascript 读取图片文件的大小
Jun 25 Javascript
jQuery 源码分析笔记(5) jQuery.support
Jun 19 Javascript
JavaScript中常用的运算符小结
Jan 18 Javascript
基于jquery的has()方法以及与find()方法以及filter()方法的区别详解
Apr 26 Javascript
jQuery实现响应浏览器缩放大小并改变背景颜色
Oct 31 Javascript
60行js代码实现俄罗斯方块
Mar 31 Javascript
jquery实现标题字体变换的滑动门菜单效果
Sep 07 Javascript
JavaScript Split()方法
Dec 18 Javascript
谈谈JavaScript中的几种借用方法
Aug 09 Javascript
antd组件Upload实现自己上传的实现示例
Dec 18 Javascript
利用JS如何获取form表单数据
Dec 19 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使用eAccelerator的API开发详解
2013/06/09 PHP
php生成毫秒时间戳的实例讲解
2017/09/22 PHP
php如何计算两坐标点之间的距离
2018/12/29 PHP
点击广告后才能获得下载地址
2006/10/26 Javascript
如何简单地用YUI做JavaScript动画
2007/03/10 Javascript
Some tips of wmi scripting in jscript (1)
2007/04/03 Javascript
JS获取dom 对象 ajax操作 读写cookie函数
2009/11/18 Javascript
使用javascript为网页增加夜间模式
2014/01/26 Javascript
javascript学习笔记(五)原型和原型链详解
2014/10/08 Javascript
JavaScript中解决多浏览器兼容性23个问题的快速解决方法
2016/05/19 Javascript
JavaScript学习笔记整理_setTimeout的应用
2016/09/19 Javascript
详解javascript中对数据格式化的思考
2017/01/23 Javascript
正则 js分转元带千分符号详解
2017/03/08 Javascript
JS中关于正则的巧妙操作
2017/08/31 Javascript
使用use注册Vue全局组件和全局指令的方法
2018/03/08 Javascript
Vue 框架之动态绑定 css 样式实例分析
2018/11/14 Javascript
Vuex中的State使用介绍
2019/01/19 Javascript
layui实现三级导航菜单
2019/07/26 Javascript
解决vue-router 二级导航默认选中某一选项的问题
2019/11/01 Javascript
python中强大的format函数实例详解
2018/12/05 Python
python使用BeautifulSoup与正则表达式爬取时光网不同地区top100电影并对比
2019/04/15 Python
Python基础学习之时间转换函数用法详解
2019/06/18 Python
基于Django框架的权限组件rbac实例讲解
2019/08/31 Python
python实现滑雪者小游戏
2020/02/22 Python
Python eval函数介绍及用法
2020/11/09 Python
浅谈Python xlwings 读取Excel文件的正确姿势
2021/02/26 Python
彪马香港官方网上商店:PUMA香港
2020/12/06 全球购物
一套VC试题
2015/01/23 面试题
渗透攻击的测试步骤
2014/06/07 面试题
毕业生求职简历的自我评价
2013/10/07 职场文书
2014年初中班主任工作总结
2014/11/08 职场文书
吧主申请感言怎么写
2015/08/03 职场文书
python - timeit 时间模块
2021/04/06 Python
JS实现简单控制视频播放倍速的实例代码
2021/04/18 Javascript
Python爬虫基础讲解之请求
2021/05/13 Python
MongoDB数据库常用的10条操作命令
2021/06/18 MongoDB