jQuery ready 方法解析

jQuery ready 方法解析


2013-11-21

不知道多少次同事让我帮忙改东西,问题的原因出在页面dom没有加载完成的时候开始对其进行操作,应对的方法不尽完美:

  1. 把执行代码放在页面底部
  2. 使用jQuery的ready方法

如果不想使用jQuery的时候就只能在页面底部引入或者留下一些代码,让人很不舒服。在网上查阅关于jQueryready方法的资料,也未得到非常满意的答案,周末在家花了点时间研究了下jQuery(参照1.10.2)源码中ready方法的实现,现在讲一下原理。

load事件

我们在熟悉的一个事件便是load了,几乎所有的浏览器都支持了这个方法。但是load有一个十分致命缺点:它会等待样式文件、图片文件和子框架页面加载完毕之后才会调用,甚至会因为资源加载的阻塞而不触发这个函数,这样的话这它就变的非常脆弱,及不保险。

DOMContentLoaded事件

另一个事件叫做DOMContentLoaded,它会在页面文档加载完毕并解析完毕的时候调用,而不会去等待样式图片等文件。这样看来用它就可以实现我们想要的效果了,当然,如果IE同意的话。

Feature Chrome Firefox (Gecko) Internet Explorer Opera Safari
Basic support 0.2 1.0 (1.7 or earlier) 9.0 9.0 3.1

除了IE之外的其它浏览器似乎问题不大,下面的重点就是IE6/7/8的兼容问题。

readystatechange事件

还好IE对这个事件支持的不错。支持readystatechange事件的对象有一个readyState属性,可能包含以下五个值中的一个:

  • uninitialized(未初始化)
  • loading(加载中)
  • loaded(加载完毕)
  • interactive(交互)
  • complete(完成)
    由于浏览器支持的不同,很难保证这五个参数会何时以何种顺序出现,不过状态complete一定是在文档交互或者完成时出现。所以只要监听readyState的状态,在complete时调用我们的函数即可。

事实上到现在为止,个人非常不严谨的测试中发现已经可以实现ready,不过jQuery中还加了一个方法。

如果没有下面的东西,我可省不少心考证。

doScroll

大多数讲解总是模糊的带过这一段,没有一个说的清清楚楚的。

IE中的doScroll方法执行时必须要求文档已经完全加载,不然就会报错,就利用这个,让document.documentElement一直调用doScroll直到它不报错的时候,去执行我们的方法,就是所谓的ready

在使用时,许多框架中都是先判断是IE并且不是在IFrame里面的时候,才去使用doScroll

到这里需要思考两个问题:第一,上面的功能已经可以实现了为什么还在这里使用doScroll?第二,使用doScroll的时候为什么还要保证不是在IFrame里面?

第一个问题还要解释点,如同时绑定readystatechangeload两个事件一样,起并行保险作用,而且貌似doScoll的反应要迅速于前两者。

关于第二个问题几乎没有讲解的,糊糊弄弄的也都说不出什么,我问过一个大牛,他说:

IFrame需要加载远程的资源,会清空dom树,因此使用doScroll不准确。

总结

所以现在ready就很明了很明了的展示在眼前了:

  1. 给非IE绑定DOMContentLoadedload事件,在触发其中一个的时候,执行ready,卸载掉所有事件绑定。
  2. IE绑定loadreadystatechange事件,在触发其中一个的时候,作判断,前者直接执行后者判断状态,之后卸载掉所有事件绑定。
  3. 满足是IE并且不在IFrame中时(是否在IFrame中可以用window.frameElement的值来判断),开始不断请求调用doScroll,在不报错时停止,执行ready,清除绑定事件。

最后

至于怎样实现多函数添加应该不是什么麻烦事儿了。

实现的写法上就当然各有千秋,找到一个非常棒的,大家可以借鉴一下(From:http://javascript.nwbox.com/ContentLoaded/ ):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
/*!
* contentloaded.js
*
* Author: Diego Perini (diego.perini at gmail.com)
* Summary: cross-browser wrapper for DOMContentLoaded
* Updated: 20101020
* License: MIT
* Version: 1.2
*
* URL:
* http://javascript.nwbox.com/ContentLoaded/
* http://javascript.nwbox.com/ContentLoaded/MIT-LICENSE
*
*/

// @win window reference
// @fn function reference
function contentLoaded(win, fn) {

var done = false, top = true,

doc = win.document, root = doc.documentElement,

add = doc.addEventListener ? 'addEventListener' : 'attachEvent',
rem = doc.addEventListener ? 'removeEventListener' : 'detachEvent',
pre = doc.addEventListener ? '' : 'on',

init = function(e) {
if (e.type == 'readystatechange' && doc.readyState != 'complete') return;
(e.type == 'load' ? win : doc)[rem](pre + e.type, init, false);
if (!done && (done = true)) fn.call(win, e.type || e);
},

poll = function() {
try { root.doScroll('left'); } catch(e) { setTimeout(poll, 50); return; }
init('poll');
};

if (doc.readyState == 'complete') fn.call(win, 'lazy');
else {
if (doc.createEventObject && root.doScroll) {
try { top = !win.frameElement; } catch(e) { }
if (top) poll();
}
doc[add](pre + 'DOMContentLoaded', init, false);
doc[add](pre + 'readystatechange', init, false);
win[add](pre + 'load', init, false);
}

}