模拟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 相关文章推荐
jquery的$(document).ready()和onload的加载顺序
May 26 Javascript
33个优秀的 jQuery 图片展示插件分享
Mar 14 Javascript
jQuery调用ajax请求的常见方法汇总
Mar 24 Javascript
基于javascript如何传递特殊字符
Nov 30 Javascript
JavaScript正则表达式匹配 div  style标签
Mar 15 Javascript
AngularJS教程 ng-style 指令简单示例
Aug 03 Javascript
原生JS实现图片轮播效果
Dec 26 Javascript
JS实现table表格固定表头且表头随横向滚动而滚动
Oct 26 Javascript
vue webpack实用技巧总结
Apr 24 Javascript
微信小程序后端(java)开发流程的详细步骤
Nov 13 Javascript
推荐几个不错的console调试技巧实现
Dec 20 Javascript
nginx部署多个vue项目的方法示例
Sep 06 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之第一天
2006/10/09 PHP
DEDE采集大师官方留后门的删除办法
2011/01/08 PHP
php数组函数序列之array_keys() - 获取数组键名
2011/10/30 PHP
jquery ready函数源代码研究
2009/12/06 Javascript
jquery实现滑动图片自己测试的例子
2013/11/05 Javascript
简介alert()与console.log()的不同
2015/08/26 Javascript
jQuery实现根据滚动条位置加载相应内容功能
2016/07/18 Javascript
详解AngularJS中的表单验证(推荐)
2016/11/17 Javascript
正则表达式替换html元素属性的方法
2016/11/26 Javascript
5分钟打造简易高效的webpack常用配置
2017/07/04 Javascript
利用HBuilder打包前端开发webapp为apk的方法
2017/11/13 Javascript
基于JavaScript实现简单的音频播放功能
2018/01/07 Javascript
Vue开发之watch监听数组、对象、变量操作分析
2019/04/25 Javascript
Openlayers显示地理位置坐标的方法
2020/09/28 Javascript
[01:03:59]2018DOTA2亚洲邀请赛3月30日 小组赛B组VGJ.T VS Secret
2018/03/31 DOTA
[48:32]2018DOTA2亚洲邀请赛 3.31 小组赛 A组 LGD vs VG
2018/04/01 DOTA
跟老齐学Python之关于循环的小伎俩
2014/10/02 Python
jupyter notebook引用from pyecharts.charts import Bar运行报错
2020/04/23 Python
django url到views参数传递的实例
2019/07/19 Python
python django生成迁移文件的实例
2019/08/31 Python
opencv之为图像添加边界的方法示例
2019/12/26 Python
pyspark给dataframe增加新的一列的实现示例
2020/04/24 Python
Schecker荷兰:狗狗用品和配件
2019/06/06 全球购物
什么是servlet链?
2014/07/13 面试题
大学校园毕业自我鉴定
2014/01/15 职场文书
文案策划求职信
2014/03/18 职场文书
原料仓管员岗位职责
2014/04/12 职场文书
单位介绍信格式
2015/01/31 职场文书
行政申诉状范文
2015/05/20 职场文书
当幸福来敲门英文观后感
2015/06/01 职场文书
法制主题班会教案
2015/08/13 职场文书
《三国志》赏析
2019/08/27 职场文书
MySQL CHAR和VARCHAR该如何选择
2021/05/31 MySQL
CSS实现五种常用的2D转换
2021/12/06 HTML / CSS
JavaScript ES6的函数拓展
2022/01/18 Javascript
游戏《东方异文石:爱亚利亚黎明》正式版发布
2022/04/03 其他游戏