模拟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版(约瑟夫环问题)
Aug 05 Javascript
编写可维护面向对象的JavaScript代码[翻译]
Feb 12 Javascript
基于jquery插件制作左右按钮与标题文字图片切换效果
Nov 07 Javascript
JavaScript Math.ceil 方法(对数值向上取整)
Jan 09 Javascript
Backbone View 之间通信的三种方式
Aug 09 Javascript
轻松搞定js表单验证
Oct 13 Javascript
使用Node.js搭建静态资源服务详细教程
Aug 02 Javascript
vue2.0 父组件给子组件传递数据的方法
Jan 15 Javascript
如何解决vue在ios微信&quot;复制链接&quot;功能问题
Mar 26 Javascript
vue.js+element 默认提示中英文操作
Nov 11 Javascript
vue实现登录、注册、退出、跳转等功能
Dec 23 Vue.js
Element el-button 按钮组件的使用详解
Feb 01 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
Ajax+PHP 边学边练之四 表单
2009/11/27 PHP
PHP自动选择 连接本地还是远程数据库
2010/12/02 PHP
php使用pack处理二进制文件的方法
2014/07/03 PHP
PHP中exec函数和shell_exec函数的区别
2014/08/20 PHP
分享50个提高PHP执行效率的技巧
2015/12/26 PHP
老生常谈ThinkPHP中的行为扩展和插件(推荐)
2017/05/05 PHP
Javascript学习笔记1 数据类型
2010/01/11 Javascript
高性能WEB开发 flush让页面分块,逐步呈现 flush让页面分块,逐步呈现
2010/06/19 Javascript
JavaScript中去掉数组中的重复值的实现方法
2011/08/03 Javascript
location对象的属性和方法应用(解析URL)
2013/04/12 Javascript
浅析JavaScript事件和方法
2015/02/28 Javascript
JS网页在线获取鼠标坐标值的方法
2015/02/28 Javascript
jquery使用ul模拟select实现表单美化的方法
2015/08/18 Javascript
同步异步动态引入js文件的几种方法总结
2016/09/23 Javascript
Websocket协议详解及简单实例代码
2016/12/12 Javascript
angularjs实现首页轮播图效果
2017/04/14 Javascript
vue-cli之router基本使用方法详解
2017/10/17 Javascript
简述Angular 5 快速入门
2017/11/04 Javascript
JS前端知识点offset,scroll,client,冒泡,事件对象的应用整理总结
2019/06/27 Javascript
js实现拖动缓动效果
2020/01/13 Javascript
[00:58]2016年国际邀请赛勇士令状宣传片
2016/06/01 DOTA
Python中的命令行参数解析工具之docopt详解
2017/03/27 Python
PYQT5设置textEdit自动滚屏的方法
2019/06/14 Python
python3下pygame如何实现显示中文
2020/01/11 Python
基于Python共轭梯度法与最速下降法之间的对比
2020/04/02 Python
Keras自动下载的数据集/模型存放位置介绍
2020/06/19 Python
用 Django 开发一个 Python Web API的方法步骤
2020/12/03 Python
澳大利亚排名第一的儿童在线玩具商店:Toy Galaxy
2018/10/06 全球购物
科研课题实施方案
2014/03/18 职场文书
《音乐之都维也纳》教学反思
2014/04/16 职场文书
文秘专业应届生求职信
2014/05/26 职场文书
标准离婚协议书(2014版)
2014/10/05 职场文书
工作作风整顿个人剖析材料
2014/10/11 职场文书
酒店办公室主任岗位职责
2015/04/01 职场文书
2019年励志签名:致拼搏路上的自己
2019/10/11 职场文书
Mysql调整优化之四种分区方式以及组合分区
2022/04/13 MySQL