模拟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 相关文章推荐
鼠标焦点离开文本框时验证的js代码
Jul 19 Javascript
JS解决ie6下png透明的方法实例
Aug 02 Javascript
分享2个jQuery插件--jquery.fileupload与artdialog
Dec 26 Javascript
jquery动态增加删减表格行特效
Nov 20 Javascript
JQuery ztree带筛选、异步加载实例讲解
Feb 25 Javascript
JS简单去除数组中重复项的方法
Sep 13 Javascript
网页挂马方式整理及详细介绍
Nov 03 Javascript
关于vuex的学习实践笔记
Apr 05 Javascript
JS正则验证多个邮箱完整实例【邮箱用分号隔开】
Apr 19 Javascript
jQuery实现全选、反选和不选功能
Aug 16 jQuery
对 Vue-Router 进行单元测试的方法
Nov 05 Javascript
vue+echarts实现多条折线图
Mar 21 Vue.js
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 form 表单传参明细研究
2009/07/17 PHP
深入解析PHP中SESSION反序列化机制
2017/03/01 PHP
Laravel如何实现适合Api的异常处理响应格式
2020/06/14 PHP
InnerHtml和InnerText的区别分析
2009/03/13 Javascript
javascript一些实用技巧小结
2011/03/18 Javascript
纯js网页画板(Graphics)类简介及实现代码
2012/12/24 Javascript
Extjs实现进度条的两种便捷方式
2013/09/26 Javascript
php显示当前文件所在的文件以及文件夹所有文件以树形展开
2013/12/13 Javascript
javascript学习笔记(五)原型和原型链详解
2014/10/08 Javascript
js树插件zTree获取所有选中节点数据的方法
2015/01/28 Javascript
js实现在网页上简单显示时间的方法
2015/03/02 Javascript
JavaScript中函数声明与函数表达式的区别详解
2016/08/18 Javascript
教大家轻松制作Bootstrap漂亮表格(table)
2016/12/13 Javascript
AngularJS控制器controller给模型数据赋初始值的方法
2017/01/04 Javascript
关于javascript作用域的常见面试题分享
2017/06/18 Javascript
react native 原生模块桥接的简单说明小结
2019/02/26 Javascript
vue2配置scss的方法步骤
2019/06/06 Javascript
js操作两个json数组合并、去重,以及删除某一项元素
2020/09/22 Javascript
nuxt.js服务端渲染中axios和proxy代理的配置操作
2020/11/06 Javascript
[25:45]2018DOTA2亚洲邀请赛4.5SOLO赛 Sylar vs Paparazi
2018/04/06 DOTA
Python操作json数据的一个简单例子
2014/04/17 Python
详解Python中DOM方法的动态性
2015/04/11 Python
python中尾递归用法实例详解
2015/04/28 Python
python中实现迭代器(iterator)的方法示例
2017/01/19 Python
python正则实现提取电话功能
2018/02/24 Python
python实现黑客字幕雨效果
2018/06/21 Python
纯CSS3发光分享按钮的实现教程
2014/09/06 HTML / CSS
html5实现完美兼容各大浏览器的播放器
2014/12/26 HTML / CSS
项目专员岗位职责
2013/12/04 职场文书
学习雷锋演讲稿
2014/05/10 职场文书
暑期培训心得体会
2014/09/02 职场文书
财务部会计岗位职责
2015/02/03 职场文书
2015年教学工作总结
2015/04/02 职场文书
CSS3实现的侧滑菜单
2021/04/27 HTML / CSS
Go遍历struct,map,slice的实现
2021/06/13 Golang
未发现nvidia显卡怎么办?Win11系统中未检测到nvidia显卡解决教程
2022/04/08 数码科技