jQuery实现DOM加载方法源码分析
传统的判断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加载方法源码分析的更多相关文章
- ElasticSearch 启动时加载 Analyzer 源码分析
ElasticSearch 启动时加载 Analyzer 源码分析 本文介绍 ElasticSearch启动时如何创建.加载Analyzer,主要的参考资料是Lucene中关于Analyzer官方文档 ...
- Springboot学习04-默认错误页面加载机制源码分析
Springboot学习04-默认错误页面加载机制源码分析 前沿 希望通过本文的学习,对错误页面的加载机制有这更神的理解 正文 1-Springboot错误页面展示 2-Springboot默认错误处 ...
- Springboot 加载配置文件源码分析
Springboot 加载配置文件源码分析 本文的分析是基于springboot 2.2.0.RELEASE. 本篇文章的相关源码位置:https://github.com/wbo112/blogde ...
- 微服务架构 | *2.3 Spring Cloud 启动及加载配置文件源码分析(以 Nacos 为例)
目录 前言 1. Spring Cloud 什么时候加载配置文件 2. 准备 Environment 配置环境 2.1 配置 Environment 环境 SpringApplication.prep ...
- springboot Properties加载顺序源码分析
关于properties: 在spring框架中properties为Environment对象重要组成部分, springboot有如下几种种方式注入(优先级从高到低): 1.命令行 java -j ...
- Spring Cloud Nacos实现动态配置加载的源码分析
理解了上述Environment的基本原理后,如何从远程服务器上加载配置到Spring的Environment中. NacosPropertySourceLocator 顺着前面的分析思路,我们很自然 ...
- Spring boot加载REACTIVE源码分析
一,加载REACTIVE相关自动配置 spring boot通过判断含org.springframework.web.reactive.DispatcherHandler字节文件就确定程序类型是REA ...
- spring启动component-scan类扫描加载过程---源码分析
http://blog.csdn.net/xieyuooo/article/details/9089441#comments
- Volley 图片加载相关源码解析
转载请标明出处: http://blog.csdn.net/lmj623565791/article/details/47721631: 本文出自:[张鸿洋的博客] 一 概述 最近在完善图片加载方面的 ...
随机推荐
- 企业shell面试题:获取51CTO博客列表倒序排序考试题
#!/bin/sh PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin HTMLFILE=/home/oldboy/ht ...
- 简单Linux命令学习笔记
1.查看进程 ps -ef | grep 关键字 /*关键字为服务名*/ netstat -unltp | grep 关键字 /*关键字为服务名或者是端口均可*/ 2.杀死进 ...
- 借助 SIMD 数据布局模板和数据预处理提高 SIMD 在动画中的使用效率
原文链接 简介 为发挥 SIMD1 的最大作用,除了对其进行矢量化处理2外,我们还需作出其他努力.可以尝试为循环添加 #pragma omp simd3,查看编译器是否成功进行矢量化,如果性能有所提升 ...
- test
http://img.ivsky.com/img/bizhi/pic/201009/07/fangaoyouhua-015.jpghttp://desk.fd.zol-img.com.cn/t_s16 ...
- 在Ubuntu13.04中配置Jexus+Mono3.2运行Asp.Net Mvc 4站点 (一)
这篇文章打算分两部分来写,第一部分介绍在Ubuntu中安装和配置.Net Framework4.5环境,第二部分介绍如何部署Asp.Net Mvc 4站点并确保Mvc4的几个重要特性都能正常工作. 一 ...
- 如何让我们的PHP在Jexus中跑起来
最近一段时间,经常看到不少的朋友在问,应该怎么设置才能够让Jexus支持PHP.其实,Jexus在很早之前就已经是可以支持PHP,像Apache或Nginx一样充当PHP的Web服务器的.不过由于没有 ...
- Mono产品生命周期
软件生命周期 同任何事物一样,一个软件产品或软件系统也要经历孕育.诞生.成长.成熟.衰亡等阶段,一般称为软件生命周期(软件生存周期) .软件生命周期模型是指人们为开发更好的软件而归纳总结的软件生命周期 ...
- UWP控件与DataBind
在uwp开发中必不可少的一个环节就是各种通用的控件的开发,所以在闲暇时间汇总了一下在uwp开发中控件的几种常用写法,以及属性的几种绑定方式,有可能不全面,请大家多多包涵 :) 1.先从win10新增的 ...
- APP技术演化的路
谈起APP,大家都太熟悉不过了,今天想谈谈这么多年技术演化的路. 早期一些大公司就开始做一些APP了,例如facebook.google等国外的公司就已经开发这个技术路线,那个时候的APP数量很少,基 ...
- Jquery双向select控件Bootstrap Dual Listbox
效果预览: 一. 下载插件 github地址:https://github.com/istvan-ujjmeszaros/bootstrap-duallistbox 也可以在这个网站中下载:http: ...