传统的判断dom加载的方法

使用 dom0级 onload事件来进行触发所有浏览器都支持在最初是很流行的写法 我们都熟悉这种写法:

window.onload=function(){

      ...

但是onload事件触发过于缓慢,尤其是在存在很多外部图片或者视频文件的时候,为了更好的了解这一点有必要知道一个html文档是如何进行加载的,这里引用一个园友的表述:

  1.用户输入网址(假设是个html页面,并且是第一次访问),浏览器向服务器发出请求,服务器返回html文件;

  2.浏览器开始载入html代码,发现<head>标签内有一个<link>标签引用外部CSS文件;

  3.浏览器又发出CSS文件的请求,服务器返回这个CSS文件;

  4.浏览器继续载入html中<body>部分的代码,并且CSS文件已经拿到手了,可以开始渲染页面了;

  5.浏览器在代码中发现一个<img>标签引用了一张图片,向服务器发出请求。此时浏览器不会等到图片下载完,而是继续渲染后面的代码;

  6.服务器返回图片文件,由于图片占用了一定面积,影响了后面段落的排布,因此浏览器需要回过头来重新渲染这部分代码;

  7.浏览器发现了一个包含一行Javascript代码的<script>标签,赶快运行它;

  8.Javascript脚本执行了这条语句,它命令浏览器隐藏掉代码中的某个<div> (style.display=”none”)。杯具啊,突然就少了这么一个元素,浏览器不得不重新渲染这部分代码;

  9.终于等到了</html>的到来,浏览器泪流满面……

  10.等等,还没完,用户点了一下界面中的“换肤”按钮,Javascript让浏览器换了一下<link>标签的CSS路径;

  11.浏览器召集了在座的各位<div><span><ul><li>们,“大伙儿收拾收拾行李,咱得重新来过……”,浏览器向服务器请求了新的CSS文件,重新渲染页面。

可以看到是先加载dom结构后加载对用的资源 比如一个一个img标签  ,浏览器再加载img标签时不会等到src对应的图片加载完成就会执行后面的代码,而onload则必须要等到所有资源加载完成才会触发,所以domContentLoaded 就代替了onload  但是对于ie低版本浏览器来说这种方法还没有实现 ,那么如何实现完美的判断dom加载呢?下面来看jquery的写法:

使用jquery进行开发

$(function(){
...
}) //or $(doucment).ready(function(){
...
})

在稍后的分析中会发现两者并无区别,下面就从这个入口开始一步一步了解jquery的写法:

源码分析

首先$(fn) 是在构造函数里传入了一个函数 在init函数(init?如果不了解jqurey构造函数可以查看博主之前的文章http://www.cnblogs.com/yy-hh/p/4492887.html

// HANDLE: $(function)
// Shortcut for document ready
} else if ( jQuery.isFunction( selector ) ) {
return rootjQuery.ready( selector );
}

如果传入的是一个函数 则会执行 rootjQuery.ready( selector );  rootjQuery是什么呢?

// All jQuery objects should point back to these
rootjQuery = jQuery(document);

其实就是$(document) ,然后执行了一个原型方法ready把函数作为参数传了进去,好的现在视线转移到ready(此方法是原型方法还有工具方法不要混淆)

ready: function( fn ) {
// Attach the listeners
jQuery.bindReady(); // Add the callback
readyList.add( fn ); return this;
},

fn接受了传递进来的函数 先执行了一个工具方法bindReady,视线接着转移

bindReady: function() {
if ( readyList ) {
return;
} readyList = jQuery.Callbacks( "once memory" ); // Catch cases where $(document).ready() is called after the
// browser event has already occurred.
if ( document.readyState === "complete" ) {
// Handle it asynchronously to allow scripts the opportunity to delay ready
return setTimeout( jQuery.ready, 1 );
} // Mozilla, Opera and webkit nightlies currently support this event
if ( document.addEventListener ) {
// Use the handy event callback
document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false ); // A fallback to window.onload, that will always work
window.addEventListener( "load", jQuery.ready, false ); // If IE event model is used
} else if ( document.attachEvent ) {
// ensure firing before onload,
// maybe late but safe also for iframes
document.attachEvent( "onreadystatechange", DOMContentLoaded ); // A fallback to window.onload, that will always work
window.attachEvent( "onload", jQuery.ready ); // If IE and not a frame
// continually check to see if the document is ready
var toplevel = false; try {
toplevel = window.frameElement == null;
} catch(e) {} if ( document.documentElement.doScroll && toplevel ) {
doScrollCheck();
}
}
},

这个方法看起来复杂,呵呵不要着急一行一行的看 我们现在的分析路线是 $(fn)->$(document).ready->$.bindReady

     if ( readyList ) {
return;
}

这里出现了一个新变量readyList  第一次执行的时候由于只有声明没有初始化肯定是undefined所以不会走这里

    // The deferred used on DOM ready
readyList,
readyList = jQuery.Callbacks( "once memory" );

然后给readyList赋值 其最为成为了一个回调对象 关于jquery回调对象的方法这里不再赘述,回调对象创建了但是目前是没有添加回调方法的

  // Catch cases where $(document).ready() is called after the
// browser event has already occurred.
if ( document.readyState === "complete" ) {
// Handle it asynchronously to allow scripts the opportunity to delay ready
return setTimeout( jQuery.ready, 1 );
}
document.readyState表示文档加载的状态,如果加载完成了则可以直接执行ready方法也是也就是执行传递的回调函数,既然已经记载好了就可以直接执行了,使用settimeout是保证函数可以异步加载

接来下来的事情就是用dom2级事件处理程序来监听onload事件 和 domcontentLoaded 既然后者加载速度比前者快为什吗还要多此一举呢?这是因为浏览器可能会缓存事件处理程序onload可能会被缓存而先执行所以都写上谁先触发谁先执行;
只不过对于domcontentLoaded是执行的domcontentLoaded方法而不是ready方法,其实domcontentLoaded方法也是最终执行ready方法 :
// Cleanup functions for the document ready method
if ( document.addEventListener ) {
DOMContentLoaded = function() {
document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false );
jQuery.ready();
}; } else if ( document.attachEvent ) {
DOMContentLoaded = function() {
// Make sure body exi msts, at least, in case IE gets a little overzealous (ticket #5443).
if ( document.readyState === "complete" ) {
document.detachEvent( "onreadystatechange", DOMContentLoaded );
jQuery.ready();
}
};
}

只不过是先解除绑定之后再执行确保不会多次触发,对于ie浏览器还有一个特殊的方法就是检测滚动条是可以可以执行 当然前提不在框架页面 ,因为如果dom结构加载好了body才有滚动条

 if ( document.documentElement.doScroll && toplevel ) {
doScrollCheck();
}
// The DOM ready check for Internet Explorer
function doScrollCheck() {
if ( jQuery.isReady ) {
return;
}
try {
// If IE is used, use the trick by Diego Perini
// http://javascript.nwbox.com/IEContentLoaded/
document.documentElement.doScroll("left");
} catch(e) {
setTimeout( doScrollCheck, 1 );
return;
} // and execute any waiting functions
jQuery.ready();
}

isReady是判断是否已经加载的状态值 只有执行ready工具方法后才会变成true,然后就是不停的检测滚动条 直不报错了执行ready方法;

所以bindReady方法就是一个准备方法,把要执行的函数绑定在回调函数中并且判断何时才能去触发,最终都执行$.ready方法 注意这里的ready是工具方法 不同于上面说的ready原型方法或者叫实例方法

马上就可以看到函数被触发了但是别着急 还没有把传进来的fn添加到回调函数列表中呢,看完bindReady之后我们再回到ready实例方法中

ready: function( fn ) {
// Attach the listeners
jQuery.bindReady(); // Add the callback
readyList.add( fn ); return this;
},

原来是在这里添加的 由于bindReady中调用jQuery.ready时都是采用的异步所以完全添加操作得以在执行之前完成 ,现在可以看最后工具方法ready了吧?当然不是你还要直到另一个方法holdReady

    // Hold (or release) the ready event
holdReady: function( hold ) {
if ( hold ) {
jQuery.readyWait++;
} else {
jQuery.ready( true );
}
},

代码不多主要就是阻止回调函数触发的,比如我们在代码中间需要加载一个脚本文件并且希望优先于rady事件执行就可以使用此方法先停止执行后再恢复实现动态脚本加载参数为false如果不传就是组织ready事件如果传入就是解除阻止,准备工作终于完成下面开始看jQuery.ready方法:

    // Handle when the DOM is ready
ready: function( wait ) {
// Either a released hold or an DOMready/load event and not yet ready
if ( (wait === true && !--jQuery.readyWait) || (wait !== true && !jQuery.isReady) ) {
// Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
if ( !document.body ) {
return setTimeout( jQuery.ready, 1 );
}

此方法接受一个参数也就是holdReady可能传入的true 这里限制了两个条件才能继续运行 1,wait为true readyWait减一后为0,readyWait是一个计数器,因为holdReady可以执行多次,没执行一次该值加一解除一次该值减一   2,wait不为true 并且isRead为false 因为isReady只用执行到这条if语句后面才能修改为ture所以这是保证不要重复执行的 。正常情况下(没有调用holdReady)都是可以通过的,如果调用了并且wait存在说明有解除但是如果解除次数低于阻止次数还是不行的;

if进来之后又是一个if判断这里是这对ie的一个bug可以忽视 有兴趣查看jQuery官网说明http://bugs.jquery.com/ticket/5443 下面就可以让isReady为true了

// 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;
}

ready状态改变之后并不意味着可以立刻执行回调函数了,在前面判断了没有使用holdReady以及使用了holdReady(false)的情况 这两种情况仅仅可以满足isReady为ture  但是如果使用了holdReady没有传值的情况时只要readyWait减一后大于0还是不能执行但是下次解除时isReady状态已经是true了

// If there are functions bound, to execute
readyList.fireWith( document, [ jQuery ] ); // Trigger any bound ready events
if ( jQuery.fn.trigger ) {
jQuery( document ).trigger( "ready" ).off( "ready" );
}

最终创建的回调对象通过fireWith方法执行了,并且把this指向了doument并且把jQuery作为参数传递了进去 最后针对有可能使用 on方法绑定ready事件也进行了trigger触发然后解除绑定;至此完毕 机构比较复杂需要看着源码多理几次,最后贴上主要源码


// 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 #6781
readyWait: 1, // Hold (or release) the ready event
holdReady: function( hold ) {
if ( hold ) {
jQuery.readyWait++;
} else {
jQuery.ready( true );
}
}, // Handle when the DOM is ready
ready: function( wait ) {
// Either a released hold or an DOMready/load event and not yet ready
if ( (wait === true && !--jQuery.readyWait) || (wait !== true && !jQuery.isReady) ) {
// Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
if ( !document.body ) {
return setTimeout( jQuery.ready, 1 );
}
// 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;
} // If there are functions bound, to execute
readyList.fireWith( document, [ jQuery ] ); // Trigger any bound ready events
if ( jQuery.fn.trigger ) {
jQuery( document ).trigger( "ready" ).off( "ready" );
}
}
}, bindReady: function() {
if ( readyList ) {
return;
} readyList = jQuery.Callbacks( "once memory" ); //
if ( document.readyState === "complete" ) {
// Handle it asynchronously to allow scripts the opportunity to delay ready
return setTimeout( jQuery.ready, 1 );
} // Mozilla, Opera and webkit nightlies currently support this event
if ( document.addEventListener ) {
// Use the handy event callback
document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false ); // A fallback to window.onload, that will always work
window.addEventListener( "load", jQuery.ready, false ); // If IE event model is used
} else if ( document.attachEvent ) {
// ensure firing before onload,
// maybe late but safe also for iframes
document.attachEvent( "onreadystatechange", DOMContentLoaded ); // A fallback to window.onload, that will always work
window.attachEvent( "onload", jQuery.ready ); // If IE and not a frame
// continually check to see if the document is ready
var toplevel = false; try {
toplevel = window.frameElement == null;
} catch(e) {} if ( document.documentElement.doScroll && toplevel ) {
doScrollCheck();
}
}
},

jQuery实现DOM加载方法源码分析的更多相关文章

  1. ElasticSearch 启动时加载 Analyzer 源码分析

    ElasticSearch 启动时加载 Analyzer 源码分析 本文介绍 ElasticSearch启动时如何创建.加载Analyzer,主要的参考资料是Lucene中关于Analyzer官方文档 ...

  2. Springboot学习04-默认错误页面加载机制源码分析

    Springboot学习04-默认错误页面加载机制源码分析 前沿 希望通过本文的学习,对错误页面的加载机制有这更神的理解 正文 1-Springboot错误页面展示 2-Springboot默认错误处 ...

  3. Springboot 加载配置文件源码分析

    Springboot 加载配置文件源码分析 本文的分析是基于springboot 2.2.0.RELEASE. 本篇文章的相关源码位置:https://github.com/wbo112/blogde ...

  4. 微服务架构 | *2.3 Spring Cloud 启动及加载配置文件源码分析(以 Nacos 为例)

    目录 前言 1. Spring Cloud 什么时候加载配置文件 2. 准备 Environment 配置环境 2.1 配置 Environment 环境 SpringApplication.prep ...

  5. springboot Properties加载顺序源码分析

    关于properties: 在spring框架中properties为Environment对象重要组成部分, springboot有如下几种种方式注入(优先级从高到低): 1.命令行 java -j ...

  6. Spring Cloud Nacos实现动态配置加载的源码分析

    理解了上述Environment的基本原理后,如何从远程服务器上加载配置到Spring的Environment中. NacosPropertySourceLocator 顺着前面的分析思路,我们很自然 ...

  7. Spring boot加载REACTIVE源码分析

    一,加载REACTIVE相关自动配置 spring boot通过判断含org.springframework.web.reactive.DispatcherHandler字节文件就确定程序类型是REA ...

  8. spring启动component-scan类扫描加载过程---源码分析

    http://blog.csdn.net/xieyuooo/article/details/9089441#comments

  9. Volley 图片加载相关源码解析

    转载请标明出处: http://blog.csdn.net/lmj623565791/article/details/47721631: 本文出自:[张鸿洋的博客] 一 概述 最近在完善图片加载方面的 ...

随机推荐

  1. css实现文本框和下拉框结合的案例

    html 代码部分 <div id="list-name-input" class="list-name-input"> <select ty ...

  2. 在配有英特尔® Iris™ 显卡的系统上通过优化对 Just Cause 3 进行增强

    高端 PC 继续通过高性能显卡驱动桌面游戏. 一流的"梦想机器"基于第六代智能 英特尔® 酷睿™ 处理器i7-6700K等 CPU,通常与高端独立显卡配合使用以运行要求最严苛的游戏 ...

  3. could not initialize proxy - no Session

    这是一个精典的问题:因为我们在hibernate里面load一个对象出来时,用到的是代理对象,也就是说当我们在执行load方法时并没有发sql语句,而是返回一个proxy对象.只有当们具体用到哪个ge ...

  4. Linux 桌面美化那点事儿

    各个 Linux 桌面发行版刚拿到手的时候,或多或少都有点儿不满意,对它们进行一些改造是必须的.网上不乏各种 Linux 桌面美化的教程和经验贴,对我们这些 Linux 爱好者来说都是很好的参考资料. ...

  5. 海量数据处理利器greenplum——初识

    简介及适用场景 如果想在数据仓库中快速查询结果,可以使用greenplum. Greenplum数据库也简称GPDB.它拥有丰富的特性: 第一,完善的标准支持:GPDB完全支持ANSI SQL 200 ...

  6. ABP(现代ASP.NET样板开发框架)系列之20、ABP展现层——动态生成WebApi

    点这里进入ABP系列文章总目录 ABP(现代ASP.NET样板开发框架)系列之20.ABP展现层——动态生成WebApi ABP是“ASP.NET Boilerplate Project (ASP.N ...

  7. 【.NET深呼吸】如何反序列化动态JSON

    .net本身除了支持SOAP.XML.二进制等序列化和反序列化,后来也加入了对JSON的序列化的支持.然而,在实际开发中,常常会遇到结构不确定的JSON对象,这些对象可能是其他代码动态生成的,你事先无 ...

  8. SQL Server-聚焦INNER JOIN AND IN性能分析(十四)

    前言 本节我们来讲讲联接综合知识,我们在大多教程或理论书上都在讲用哪好,哪个性能不如哪个的性能,但是真正讲到问题的实质却不是太多,所以才有了本系列每一篇的篇幅不是太多,但是肯定是我用心去查找许多资料而 ...

  9. SQL Server-数据类型(七)

    前言 前面几篇文章我们讲解了索引有关知识,这一节我们再继续我们下面内容讲解,简短的内容,深入的理解,Always to review the basics. 数据类型 SQL Server支持两种字符 ...

  10. ITTC数据挖掘平台介绍(四) 框架改进和新功能

    本数据挖掘框架在这几个月的时间内,有了进一步的功能增强 一. 超大网络的画布显示虚拟化     如前几节所述,框架采用了三级层次实现,分别是数据,抽象Node和绘图的DataPoint,结构如下:   ...