最近两天不忙的时候再回过来研究一下jquery的源码,看到$(document).ready()时,深入的研究了一下dom的加载问题。
我们都知道,window.onload可以解决我们的js执行时机问题。有了它,可以把代码放在页面的任何位置。但是我们也知道,window.onload 要在html、css、js、img全部加载完后才会触发,而这样会影响页面的访问速度。因此,jquery通过封装$(function)做到了DOM加载完毕时就对js内容进行触发。
那jquery做到的原理是什么呢?我从原理上简单的做一下分析。
在IE9+、chorme、firefox等现代浏览器中,有个原生的js事件:DOMContentLoaded 当这个事件触发时,就说明DOM已经加载完毕,可以进行下面的js操作了
document.addEventListener('DOMContentLoaded',function(){alert(123);},false);
但是IE8以下是不支持这个事件的,所以IE用的是onreadystatechange事件
document.onreadystatechange = function(){ if(document.readyState == 'complete'){ alert('Dom已经加载完毕') } }
但是,非标准浏览器不会这么容易让我们好过。
首先,当document.readyState == 'complete'时,图片等文件已经加载完成了,虽然会在window.onload 之前执行,但是对交互效果来说还是触发的太晚了。有些站点的图片要下载下来是很慢很慢滴!
我在这里随便举个例子:
<body> <div>12345555555555555555555555555</div> <img src="http://b.hiphotos.baidu.com/zhidao/pic/item/c8177f3e6709c93da9f2dafa9b3df8dcd10054bf.jpg" alt=""> <script> alert(111) window.onload = function(){ alert(456) } document.onreadystatechange = function(){ if(document.readyState == 'complete'){ alert(123); //complete是指所有资源包括图片js等文件全部加载完成后才会触发。 //readyState == 'complete'确实会在onload前触发,但是还是触发的太晚。 } } </script> </body>
大家看,在IE8下,当页面执行时,首先弹出111,这毫无疑问。然后呢?对不起,是图片在页面中完全显示出来。然后再弹出123,最后弹出456.
再次,alert(123)执行时机也不确定,因为同一段代码,再IE7中,或者更高层的IE9种,有时图片还没有加载下来,alert(123)执行了。所以单纯使用这个readyState不太靠谱
那么jquery中是如何做的呢?jquery中用了一个IE才能识别的属性:document.documentElement.doScroll
这是个什么东东?好像从来没有见过? 这就是学习源码的好处,可以看看大师级人物是如何做处理的,并且我们可以从中取其精华,好不惬意!
兼容不支持该事件的浏览器
在IE8中,可以使用readystatechange
事件来检测DOM文档是否加载完毕.在更早的IE版本中,可以通过每隔一段时间执行一次document.documentElement.doScroll("left")来检测这一状态,
因为这条代码在DOM加载完毕之前执行时会抛出错误(throw an error)。
doScroll通过时readyState可能为interactive, 也可能为complete. 但是一定会在DOM结构稳定后, 图片加载完毕前执行.
// 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();
}
这是jquery的源码,通过异常捕获,如果DOM没有加载完,则会一直catch这个setTimeout ,直到dom加载完,执行以下try里面的doScroll
具体doScroll的详细含义大家可以从网上查查,我这里就不列举了
MSDN 关于 JScript 的一个方法有段不起眼的话,当页面 DOM 未加载完成时,调用 doScroll 方法时,会产生异常。那么我们反过来用,如果不异常,那么就是页面DOM加载完毕了!
当然,doScroll的运用前提是页面中没有iframe,如果有iframe,那么只能是图片之类的全加载完毕才执行了。