模拟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 选项卡效果(JS与HTML的分离)
Apr 01 Javascript
自定义右键属性覆盖浏览器默认右键行为实现代码
Feb 02 Javascript
js实现获取焦点后光标在字符串后
Sep 17 Javascript
angularjs学习笔记之完整的项目结构
Sep 26 Javascript
JavaScript暂停和继续定时器的实现方法
Jul 18 Javascript
Three.js利用顶点绘制立方体的方法详解
Sep 27 Javascript
angular.js实现列表orderby排序的方法
Oct 02 Javascript
解决layui页面按钮点击无反应,也不报错的问题
Sep 29 Javascript
vue el-table实现自定义表头
Dec 11 Javascript
Javascript ParentNode和ChildNode接口原理解析
Mar 16 Javascript
node.js通过Sequelize 连接MySQL的方法
Dec 28 Javascript
Node快速切换版本、版本回退(降级)、版本更新(升级)
Jan 07 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短域名转换为实际域名函数
2011/01/17 PHP
提高php运行速度的一些小技巧分享
2012/07/03 PHP
免费的ip数据库淘宝IP地址库简介和PHP调用实例
2014/04/08 PHP
php_screw安装使用教程(另一个PHP代码加密实现)
2014/05/29 PHP
浅谈php和.net的区别
2014/09/28 PHP
PHP PDOStatement::fetchAll讲解
2019/01/31 PHP
Nigma vs Liquid BO3 第二场2.13
2021/03/10 DOTA
javascript 继承实现方法
2009/08/26 Javascript
用Javascript实现Windows任务管理器的代码
2012/03/27 Javascript
jquery ui dialog实现弹窗特效的思路及代码
2013/08/03 Javascript
JavaScript的Module模式编程深入分析
2013/08/13 Javascript
深入探寻javascript定时器
2015/01/02 Javascript
为什么JS中eval处理JSON数据要加括号
2015/04/13 Javascript
AngularJs动态加载模块和依赖注入详解
2016/01/11 Javascript
jQuery插件uploadify实现ajax效果的图片上传
2016/06/18 Javascript
Vue 单文件中的数据传递示例
2017/03/21 Javascript
微信小程序使用form表单获取输入框数据的实例代码
2018/05/17 Javascript
微信小程序实现的绘制table表格功能示例
2019/04/26 Javascript
微信小程序canvas分享海报功能
2019/10/31 Javascript
vue 使用 vue-pdf 实现pdf在线预览的示例代码
2020/04/26 Javascript
深入了解Vue动态组件和异步组件
2021/01/26 Vue.js
python批量生成本地ip地址的方法
2015/03/23 Python
python通过定义一个类实例作为ftp回调方法
2015/05/04 Python
浅谈flask源码之请求过程
2018/07/26 Python
使用Python+Appuim 清理微信的方法
2021/01/26 Python
美国最受欢迎的童装品牌之一:The Children’s Place
2016/07/23 全球购物
Keds加拿大官网:购买帆布运动鞋和皮鞋
2019/09/26 全球购物
户外用品商店创业计划书
2014/01/29 职场文书
酒店管理求职信范文
2014/04/06 职场文书
房屋出售协议书
2014/04/10 职场文书
大学生心理活动总结
2014/07/04 职场文书
三峡导游词
2015/01/31 职场文书
小学语文继续教育研修日志
2015/11/13 职场文书
Vue实现跑马灯样式文字横向滚动
2021/11/23 Vue.js
java协程框架quasar和kotlin中的协程对比分析
2022/02/24 Java/Android
Python Pygame实战之塔防游戏的实现
2022/03/17 Python