jquery源码 DOM加载

简介:

jQuery版本:2.0.3

DOM加载有关的扩展

  • isReady:DOM是否加载完(内部使用) 

  • readyWait:等待多少文件的计数器(内部使用)

  • holdReady():推迟DOM触发

  • ready():准备DOM触发。

  • jQuery.ready.promise=function(){};  监听DOM的异步操作(内部使用)

一、$(function(){})和原生window.onload的关系

这个在面试中也是经常会被问到的。从下面几个角度来分析一下它们的区别

1、执行时机

页面加载,先加载节点,再加载文件,比如img文件,flash等。

$(function(){})DOM加载完执行。可能DOM元素关联的东西并没有加载完。

window.onload等节点和文件都加载完执行。

对应的事件监听

jQuery用的是DOMContentLoaded事件。

DOMDContentLoaded:原生DOM加载事件,这个事件触发代表DOM加载完了。

我之前写过一篇文章的页面加载时间分析里也有提到。

onload对应的是load事件。

2、个数

window.onload不能写多个,后面的会覆盖前面的。

$(function(){})可以写多个。都会执行 。

3、简化写法

$(function(){})是$(document) .ready(function(){});的简化写法。

window.onload没有简化写法。

二、jQurey如何实现DOM ready的

jQuery直接调用DOMContentLoaded来实现DOM的ready。但是DOMContentLoaded和onLoad一样,浏览器只执行一次,jQuery用什么判断是否已经执行过呢?document.readyState就是判断这个的依据。

readyState是document的属性,总共有3个值:

  • loading:文档正在加载中

  • interactive:文档已经加载完成,正在进行css和图片等资源的加载

  • complete:文档的所以资源加载完成

判断完之后如何回调呢?就是用Promise。jQuery通过new一个$.Deferred(promise)对象来实现对DOM的ready的回调,在DOMContentLoaded中将这个promise给resolve掉,这样就执行了之前注册的回调函数,同时后面新注册的回调也会立刻执行。

但是在调用promise之前,jQuery执行了一次setTimeout,因为jQuery.Promise是不会产生异步的,这和标准的promise规范是不一样的,所有jQuery自己又手动做了一次setTimeout来实现异步。这样使得无论使用在DOM的ready之前注册的回调还是之后注册的回调都会在异步中执行。

三、源码整体实现逻辑

$(fn)==>new一个 jQuery.fn.init(fn)==>返回$(document).ready( fn)。也就是说

  • $(function(){}) =》

  • 调用$(document).ready(function(){})=》

  • 相当于$().ready(fn)实例方法=》

  • 调用的jQuery.ready.promise().done(fn)=》

  • jQuery.ready.promise中不管if还是else最终都是调用jQuery.ready(),并返回promise=》

  • jQuery.ready()里面重点是readyList.resolveWith( document, [ jQuery ] );已完成。至此DOM加载完毕就可以调用fn了。

 

 

 

四、实现细节

1、重点是:jQuery.ready.promise()方法【巧妙】

如果DOM已经加载完成了,调用jQuery.ready()这个工具方法;

如果DOM没加载完,监听DOMContentLoaded事件和load事件,等事件发生时回调completed(),最终也是调用jQuery.ready()这个工具方法;

复制代码

var    // A central reference to the root jQuery(document)    rootjQuery,    // The deferred used on DOM ready    readyList;

jQuery.ready.promise = function( obj ) {    if ( !readyList ) { //第一次readyList为空可以进来,后续就进不来if了,只执行一次
        readyList = jQuery.Deferred(); //第一步,创建延迟对象
        if ( document.readyState === "complete" ) { //DOM加载完成的标志就是document.readyState为complete,如果DOM已经加载好了就直接调工具方法jQuery.ready。
            setTimeout( jQuery.ready );//加定时器是为了兼容IE
        } else {//DOM没有加载完检测,即检测了DOMContentLoaded事件,也检测了load事件;最终走回调completed函数
            // Use the handy event callback
            document.addEventListener( "DOMContentLoaded", completed, false );            // A fallback to window.onload, that will always work
            window.addEventListener( "load", completed, false ); //因为火狐浏览器会缓存load事件,为了第一时间相应所以对load也监听了        }
    }    return readyList.promise( obj );
};

复制代码

completed回调函数如下,最终调用的也是jQuery.ready()。 

复制代码

    // The ready event handler and self cleanup method
    completed = function() {    //不管是DOMContentLoaded事件还是load发生,都会取消2个事件监听
    //jQuery.ready()只会触发一次
        document.removeEventListener( "DOMContentLoaded", completed, false );
        window.removeEventListener( "load", completed, false );
        jQuery.ready();
    };

复制代码

2、jQuery.ready()工具方法做了些什么 

先做个测试:

   $(function(arg){
       alert(this); //[object HTMLDocument]
       alert(arg); //jQuery函数
   })

再做个测试:

除了$(function(){});$(document).ready(function(){}),也可以把ready当做事件来处理如下。

<script>$(document).on("ready",function(){
    alert(123); //123});</script>

之所以会自动弹出123,是因为在jQuery源码中用这句话jQuery( document ).trigger("ready").off("ready");来主动触发了ready事件,触发后再取消掉。

复制代码

// Handle when the DOM is readyready: function( wait ) { //参数和holdReady有关

    // Abort if there are pending holds or we're already ready
    if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {        return;
    }    // Remember that the DOM is ready
    jQuery.isReady = true;    // If a normal DOM Ready event fired, decrement, and wait if need be
    if ( wait !== true && --jQuery.readyWait > 0 ) {        return;
    }    //第一步看这里重点,resolveWith改变状态的时候传参了,给done中方法fn传入了参数,
    //document是fn的this指向,jQuery是参数
    // If there are functions bound, to execute    readyList.resolveWith( document, [ jQuery ] );    //跟主动触发有关
    // Trigger any bound ready events
    if ( jQuery.fn.trigger ) {
        jQuery( document ).trigger("ready").off("ready");
    }
},

复制代码

跟ready的参数有关的有一个holdReady()。

先做个测试

$.holdReady(true);
$(function () {
    alert(123); //调用了holdReady并传参true,就不能弹出123了});

可以推迟,也可以释放推迟,释放了以后就可以触发了。

$.holdReady(true);
$.holdReady(false);
$(function () {
    alert(123); //释放了holdReady,就弹出123});

这个有什么用?

比如:

复制代码

$.getScript('js/a.js',function(){ //异步加载,不会影响后续代码执行。可能会产生一个问题,alert(2)先执行了a.js还没有加载完})

$(function () {
    alert(2);//先弹2,后弹出1});

复制代码

很多时候引入外部文件的时候,都想等外部文件或者插件加载完,再去触发操作,操作可能用到a.js。现在这个顺序不对,会出问题。

怎样解决这个问题?用holdReady()方法。

复制代码

    $.holdReady(true); //在这里先hold住
    $.getScript('js/a.js', function () { //异步加载,不会影响后续代码执行。可能会产生一个问题,alert(2)先执行了a.js还没有加载完
        $.holdReady(false); //加载完释放,不hold了就可以弹2了    })

    $(function () {
        alert(2);//先弹2,后弹出1
    });

复制代码

再深入一点,holdReady() 要针对的文件可能不止一个,有很多个,所以要等所有的文件都加载完再执行,所以源码中有一个计数的readyWait。

源码中定义了一个等待栈变量——readyWait,每次执行$.holdReady(true)都会增加压栈,而每次$.holdReady()执行都会弹栈,等空栈的时候就执行jQuery.ready函数,即将promise给resolve掉。

复制代码

// Is the DOM ready to be used? Set to true once it occurs.isReady: false,// A counter to track how many items to wait for before// the ready event fires. See #6781readyWait: 1,//第二步看这里//holdReady推迟DOM的触发// Hold (or release) the ready eventholdReady: function( hold ) {    if ( hold ) {
        jQuery.readyWait++;//hold为真,让readyWait加加处理
    } else {
        jQuery.ready( true );
    }
},// Handle when the DOM is readyready: function( wait ) { //参数和holdReady有关

    // Abort if there are pending holds or we're already ready
    //readyWait为0时就不用hold了,执行后面的操作
    if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { 
        return;
    }    // Remember that the DOM is ready
    jQuery.isReady = true; //默认是false

    // If a normal DOM Ready event fired, decrement, and wait if need be
    if ( wait !== true && --jQuery.readyWait > 0 ) {        return;
    }    //第一步看这里重点,resolveWith改变状态的时候传参了,给done中方法fn传入了参数,
    //document是fn的this指向,jQuery是参数
    // If there are functions bound, to execute    readyList.resolveWith( document, [ jQuery ] );    //跟主动触发有关
    // Trigger any bound ready events
    if ( jQuery.fn.trigger ) {
        jQuery( document ).trigger("ready").off("ready");
    }

},


本文转自 sshpp 51CTO博客,原文链接:http://blog.51cto.com/12902932/1926151,如需转载请自行联系原作者

相关文章
|
2月前
|
JavaScript 前端开发 开发者
深入了解jQuery:轻松实现高效的DOM操作
【10月更文挑战第11天】深入了解jQuery:轻松实现高效的DOM操作
63 0
|
1月前
|
JavaScript
jQuery制作的3D冰块立方时钟动态特效源码
jQuery制作的3D冰块立方时钟动态特效源码是一段基于jQuery实现的3D魔方立方时钟效果代码,该设计非常特别,且支持数字颜色的变化,提供8款颜色选择,非常有意思,欢迎对此段代码感兴趣的朋友前来下载使用。
36 8
|
1月前
|
JavaScript
jQuery制作的网站首页宽屏导航轮播图特效源码
jQuery制作的网站首页宽屏导航轮播图特效源码是一段基于jQuery制作的可用于商城首页 微商城 互联网公司或某些电子商城的首页特效,自带有二级菜单栏、轮播图滚动、登录注册按钮等等,非常全面,欢迎对此段代码感兴趣的朋友前来下载使用。
20 4
|
1月前
|
JavaScript
jQuery实现的手风琴四季场景变换特效源码
jQuery实现的手风琴四季场景变换特效源码是一段基于jQuery实现的手风琴四季场景变换效果代码,拥有春夏秋冬四季转场特效,鼠标可放大每个季节的图像,欢迎对此段代码感兴趣的朋友前来下载使用。
25 1
|
3月前
|
JavaScript 前端开发
jQuery 操作 DOM 及 CSS
本文介绍了如何使用jQuery进行DOM操作和CSS样式的修改,包括如何获取和设置元素的文本内容、属性值、添加和删除元素,以及如何使用jQuery的addClass、removeClass、toggleClass、hasClass和css方法来操作元素的CSS。通过示例代码,展示了jQuery在实际开发中的便捷性。
jQuery 操作 DOM 及 CSS
|
3月前
|
JSON JavaScript 前端开发
Jquery常用操作汇总,dom操作,ajax请求
本文汇总了jQuery的一些常用操作,包括DOM元素的选择、添加、移除,表单操作,以及如何使用jQuery发送Ajax请求,涵盖了GET、POST请求和文件上传等常见场景。
|
2月前
|
JavaScript 前端开发 API
深入理解jQuery:高效DOM操作与事件处理
【10月更文挑战第11天】深入理解jQuery:高效DOM操作与事件处理
22 0
|
2月前
|
移动开发 JSON 数据可视化
精选八款包括可视化CMS,jquery可视化表单,vue可视化拖拉,react可视化源码
精选八款包括可视化CMS,jquery可视化表单,vue可视化拖拉,react可视化源码
54 0
|
5月前
|
JavaScript 前端开发 API
前端框架与库 - jQuery基础与DOM操作
【7月更文挑战第18天】jQuery 是一个简化JavaScript任务的库,以其“write less, do more”理念著称。核心功能包括DOM操作、事件处理和Ajax。DOM操作如选择元素(`$(&quot;p&quot;)`、`$(&quot;#myDiv&quot;)`、`$(&quot;.myClass&quot;)`)、创建及添加元素、修改属性和内容。事件处理如绑定(`click`)和触发(`trigger`)。常见问题涉及`$`符号冲突(使用`jQuery`代替)、异步加载管理和选择器性能优化。了解并规避这些问题能提升jQuery使用效率。
36 0
N..
|
7月前
|
JavaScript 前端开发 容器
jQuery中的DOM操作
jQuery中的DOM操作
N..
74 1