不管是我们使用 jQuery 还是其他任何的库,我们大部分都会使用到 ready 的事件,因为很多的代码只能在 DOM 结构加载完成后才能执行,否则就会报错。因此都会封装跨浏览器兼容性的 ready 事件,不过本篇文章主要是讲解 jQuery 中的 ready 事件。 上面已经说了,“有些代码只能在 DOM 结构加载完成后才能执行”,不过有的人可能不是太理解,这里我们来举一个简单的例子 我们在页面中运行上面的代码,控制台就报错: 如果只是特别单纯简单的 js 代码,从上往下执行的那种,直接放在 body 的后面,是没必要使用 ready 的;但是,更多的情况,是没有那么多简单的 js 代码的,比如有的时候需要把 js 代码放在前面,有时需要延迟执行一些 js 代码,等等,这些就需要用到 ready 事件了。 jQuery 中的 ready 事件通常有两种书写方式,效果都是一样的: 简写方式: 完整的方式: 其实在 jQuery 的内部,简写的方式最终也是通过调用下面的方式的。我们来看 jQuery 的源码里: 在 则调用 即: 我们在上面已经了解了 jQuery 的 ready 的使用方法,在这一节我们来了解下 ready 的实现机制: DOMContentLoaded 是当所有 DOM 结构解析完,而不用等待其他的资源(如图片,PDF 等)加载完成,就可以触发这个事件;所有的高级浏览器都支持此事件,只有低版本的 IE 不支持此事件; ie6~8 可以使用 document.onreadystatechange 事件监听 document.readyState 状态是否等于 complete 来判断 DOM 是否加载完毕,如果页面中嵌有 iframe 的话,ie6~8 的 document.readyState 会等到 iframe 中的所有资源加载完才会变成 complete,此时 iframe 变成了耗时大户。但是经过测试,即使页面中没有 iframe,当 readyState 等于 complete 时,实际触发的是 onload 事件而不是 DOMContentLoaded 事件,对这点表示惊奇。 还好 ie 有个特有的 doScroll 方法,当页面 DOM 未加载完成时,调用 doScroll 方法时,就会报错,反过来,只要一直间隔调用 doScroll 直到不报错,那就表示页面 DOM 加载完毕了,不管图片和 iframe 中的内容是否加载完毕,此法都有效。 当我们了解了上面的知识后,就容易理解 jQuery 的 ready 设计机制了。 在上面的方法中,不论是触发 DOMContentLoaded,还是 load,还是 onreadystatechange,都会执行 看着写了挺多,其实还有很多没有说到点子上,只是写了一些自己的理解罢了!!1. 为什么要使用 ready #
<script>var time = document.getElementById('time');
time.innerHTML = Date.now();script>
<body>
<div id="time">div>
body>
Uncaught TypeError: Cannot set property 'innerHTML' of null
,意思就是不能给不存在的对象设置 innerHTML 属性。这是因为当代码执行到document.getElementById('time')
时,id 为 time 的 DOM 元素还没加载,找不到 time 这个元素,代码就报错了;正确的方式就是把 js 代码放到 body 的后面,这样就能保证所有的 DOM 元素都执行完后才执行 js 代码。2. ready 的书写方式 #
$(function () {
// ready
});
// 给document元素绑定ready事件
$(document).ready(function () {});
// HANDLE: $(function)
// Shortcut for document ready
if () {
} else if ( jQuery.isFunction( selector ) ) {
return typeof rootjQuery.ready !== "undefined" ?
rootjQuery.ready( selector ) :
// Execute immediately if ready is not present
selector( jQuery );
}
$(selector)
里,若参数 selector 是 function 类型的,即$(function () {});
function ready() {}
$(ready);
rootjQuery.ready( selector )
,而且:rootjQuery = jQuery(document);
jQuery(document).ready(function () {});
3. ready 的触发机制是什么 #
document.readyState==complete
来判断 DOM 是否加载完成,但是低版本的 firefox 不支持此属性:
// Catch cases where $(document).ready() is called after the browser event has already occurred.
// we once tried to use readyState "interactive" here, but it caused issues like the one
// discovered by ChrisS here: http://bugs.jQuery.com/ticket/12282#comment:15
// 曾经打算用document.readyState === "interactive"来判断DOM是否加载完成,但是会出现bug,因此使用complete
// 如果当前状态已经是complete里,就直接执行
if (document.readyState === 'complete') {
// Handle it asynchronously to allow scripts the opportunity to delay ready
// 异步执行ready方法,以保证脚本有机会延迟执行(holdReady)
setTimeout(jQuery.ready);
// Standards-based browsers support DOMContentLoaded
// 支持addEventListener的浏览器都支持DOMContentLoaded,直接调用DOMContentLoaded即可
} else if (document.addEventListener) {
// Use the handy event callback
document.addEventListener('DOMContentLoaded', completed, false);
// A fallback to window.onload, that will always work
// 有的浏览器会缓存load方法,下一次加载时load有可能优先于DOMContentLoaded触发
// 因此两个都写上,哪个先触发就走哪个
// 但是两个都写上,就不怕触发两次么,先把这段代码看完,然后看第4节
window.addEventListener('load', completed, false);
// If IE event model is used
// 如果当前是IE浏览器
} else {
// Ensure firing before onload, maybe late but safe also for iframes
// 绑定onreadystatechange以监听readyState的变化
document.attachEvent('onreadystatechange', completed);
// A fallback to window.onload, that will always work
window.attachEvent('onload', completed);
// If IE and not a frame
// continually check to see if the document is ready
// 如果当前页面中没有frame,则持续检查是否可以调用doScroll方法以监听document是否加载完成
var top = false;
try {
top = window.frameElement == null && document.documentElement;
} catch (e) {}
if (top && top.doScroll) {
(function doScrollCheck() {
if (!jQuery.isReady) {
try {
// Use the trick by Diego Perini
// http://javascript.nwbox.com/IEContentLoaded/
top.doScroll('left');
} catch (e) {
return setTimeout(doScrollCheck, 50);
}
// detach all dom ready events
detach();
// and execute any waiting functions
jQuery.ready();
}
})();
}
}
4. completed 方法 #
completed
,那这个方法里都干了些什么呢?我们来看看。/**
* The ready event handler and self cleanup method
*/
function completed() {
// readyState === "complete" is good enough for us to call the dom ready in oldIE
// 即使在老版IE下readyState === "complete"也足以说明DOM加载完成
if (document.addEventListener || event.type === 'load' || document.readyState === 'complete') {
detach(); // 执行detach函数
jQuery.ready(); // 执行ready
}
}
/**
* Clean-up method for dom ready events
*/
// 从这个方法里可以看到,不论是先执行DOMContentLoaded还是先执行load,都会把两个事件同时解除掉,因此这两个事件最多只能触发一次;IE下同理
function detach() {
if (document.addEventListener) {
document.removeEventListener('DOMContentLoaded', completed, false);
window.removeEventListener('load', completed, false);
} else {
document.detachEvent('onreadystatechange', completed);
window.detachEvent('onload', completed);
}
}
版权属于:
加速器之家
作品采用:
《
署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)
》许可协议授权
评论