模拟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互斥锁分享
Feb 02 Javascript
Javascript闭包实例详解
Nov 29 Javascript
原生javascript实现addClass,removeClass,hasClass函数
Feb 25 Javascript
jQuery Mobile操作HTML5的常用函数总结
May 17 Javascript
AngularJS控制器之间的数据共享及通信详解
Aug 01 Javascript
Javascript之面向对象--封装
Dec 02 Javascript
详解JavaScript数组过滤相同元素的5种方法
May 23 Javascript
JS HTML图片显示Canvas 压缩功能
Jul 21 Javascript
解决jquery appaend元素中id绑定事件失效的问题
Sep 12 jQuery
微信小程序倒计时功能实现代码
Nov 09 Javascript
如何解决js函数防抖、节流出现的问题
Jun 17 Javascript
一小时迅速入门Mybatis之bind与多数据源支持 Java API
Sep 15 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操作XML、读取数据和写入数据的实现代码
2014/08/15 PHP
PHP HTTP 认证实例详解
2016/11/03 PHP
php+redis实现消息队列功能示例
2019/09/19 PHP
PHP实现简单日历类编写
2020/08/28 PHP
javascript Base类 包含基本的方法
2009/07/22 Javascript
json格式化/压缩工具 Chrome插件扩展版
2010/05/25 Javascript
JavaScript 错误处理与调试经验总结
2010/08/10 Javascript
深入剖析JavaScript面向对象编程
2016/07/12 Javascript
js图片上传前预览功能(兼容所有浏览器)
2016/08/24 Javascript
Bootstrap jquery.twbsPagination.js动态页码分页实例代码
2017/02/20 Javascript
nodejs制作爬虫实现批量下载图片
2017/05/19 NodeJs
React-router 4 按需加载的实现方式及原理详解
2017/05/25 Javascript
详解vue-cli与webpack结合如何处理静态资源
2017/09/19 Javascript
JavaScript设计模式之原型模式分析【ES5与ES6】
2018/07/26 Javascript
在Echarts图中给坐标轴加一个标识线markLine
2020/07/20 Javascript
[01:15]PWL S2开团时刻第二期——他们杀 我就白给
2020/11/25 DOTA
python解析文件示例
2014/01/23 Python
python实现的简单RPG游戏流程实例
2015/06/28 Python
Python 爬虫爬取指定博客的所有文章
2016/02/17 Python
Python新手们容易犯的几个错误总结
2017/04/01 Python
Django中自定义查询对象的具体使用
2019/10/13 Python
Django框架模板用法入门教程
2019/11/04 Python
关于Python中定制类的比较运算实例
2019/12/19 Python
python GUI库图形界面开发之PyQt5单行文本框控件QLineEdit详细使用方法与实例
2020/02/27 Python
CSS3实现银灰色动画效果的导航菜单代码
2015/09/01 HTML / CSS
Bibloo匈牙利:女装、男装、童装及鞋子和配饰
2019/04/14 全球购物
加拿大著名的奢侈品购物网站:SSENSE(支持中文)
2020/06/25 全球购物
优秀干部获奖感言
2014/01/31 职场文书
海洋科学专业求职信
2014/08/10 职场文书
单位工作证明范文
2014/09/14 职场文书
2015年乡镇财政工作总结
2015/05/19 职场文书
外出培训学习心得体会
2016/01/18 职场文书
演讲稿:​快乐,从不抱怨开始!
2019/04/02 职场文书
Python pyecharts绘制条形图详解
2022/04/02 Python
【DOTA2】总决赛血虐~ XTREME GAMING vs MAGMA - OGA DOTA PIT 2022 CN
2022/04/02 DOTA
Android Flutter实现3D动画效果示例详解
2022/04/07 Java/Android