window.onload与$(document).ready()的区别
对于初学者来说,window.onload并不陌生,但是对于为什么要使用它、以及不使用它会导致什么结果,可能并没有深究过,下面通过几个实验对比,深刻理解。
实验一:
<script> document.getElementById("me").style.color = "blue"; </script> <body> <h1 id="me">Double Zhang</h1> </body>
实验结果是 h1字体为原始黑色,并没有被渲染为蓝色,为什么代码没有生效呢?
首先要知道,document文档是从上向下解析执行,当执行到script时,body中的h1还未被执行和加载到网页中,这时候 document.getElementById("me") 并没有获取到h1对象,可以打断点看一下,获取的值是undefined,那就更不存在将它的字体设置为蓝色了。
实验二:
<body> <script> document.getElementById("me").style.color = "blue"; </script> <h1 id="me">Double Zhang</h1>
</body>
是不是把script标签放到body里就可以获取到元素了呢?实验结果字体依旧是原始的黑色。
“document文档是从上向下解析执行”,当执行script时,h1仍旧没有被加载。
实验三:
<body> <h1 id="me">Double Zhang</h1> <script> document.getElementById("me").style.color = "blue"; </script> </body>
实验结果字体为蓝色,终于渲染成功。
注意,这次代码执行的顺序有所改变,先将h1标签渲染进DOM树,接着才是通过JS获取,所以获取到了该元素,接着改变字体颜色。
实验四:
<script>
window.onload = function(){ document.getElementById("me").style.color = "blue"; }
</script>
<body> <h1 id="me">Double Zhang</h1> </body>
实验结果:JS代码还是在body标签前面,讲道理这时候是获取不到h1标签的,但是这次渲染成功了,字体为蓝色。
仔细看过代码后发现此次实验使用了window.onload,这是成功渲染的关键,这里有必要解释一下window.onload了,它表示:网页完成了DOM树的创建以及网页中图片等外部资源的加载。其中完成DOM树的创建可以保证我们能通过getElementById方法在DOM树中找到元素,这就是为什么实验四能够成功渲染蓝色字体。
有了这个神器,妈妈再也不用担心写在HTML前面的JS代码获取不到我们想要的元素了,不过先别高兴的太早,因为这里存在一个隐患,window.onload中的代码不仅是在DOM树创建后执行,而且还要等到图片资源加载完成,那么当页面中需要加载的文件(图片、文本……)非常多的时候,window.onload里面的代码就迟迟无法执行,这将带来很差的用户体验,于是我们会思考,我们只是想通过JS代码操作DOM,与图片并没有关系啊,所以能不能让DOM树一创建完成就执行代码而不用等图片加载完毕呢?毕竟我们都想让自己的网页性能更好一些、加载更快一些、用户体验更好一些,那就接着往下看吧。
实验五:
<script> setTimeout(function(){ document.getElementById("me").style.color = "blue"; },3000); </script> <body> <h1 id="me">Double Zhang</h1> </body>
实验结果:字体成功渲染为蓝色,而且没有使用window.onload。
这里做了一个定时器的3秒延时,在这3秒里,页面完成了DOM树的创建,所以h1元素被成功获取到了,再添加颜色就没问题了,但是在这3秒里该网页的图片资源有没有全部加载完成并不能确定,不过至少DOM树是创建完成了,否则实验结果不会正确。(注意这个3秒的取值是随便取的,没有任何规定,纯属测试)
细心的人会发现这个实验有点小问题,运行代码,会看到字体会闪一下,闪之前为黑色,闪之后变为蓝色,因为页面刚呈现时h1默认为黑色,三秒时代码生效,字体被设置为红色。这种用户体验并不好,不知是否可以改进?
总结一下这种方法的两大隐患,第一,因为我们无法把握DOM树完成创建的准确时间,就意味着我们不能避免“闪一下”的问题,和实验五一样,假如我们设置的延迟大于DOM完成创建需要的时间,那页面就会闪一下;第二,假如我们设置的setTimeout延迟时间小于DOM树完成创建需要的时间,DOM未创建好,那么字体也不会被渲染为蓝色。带着消除隐患的念头,接着往下看吧。
实验六:
<script> $(document).ready(function(){ document.getElementById("me").style.color = "blue"; }); </script> <body> <h1 id="me">Double Zhang</h1> </body>
实验结果:字体成功渲染蓝色,使用了jQuery的$.ready()方法。
说到这里,本篇文章的大BOSS终于出场了(脑中请自行响起BGM)。
$.ready(),它表示:在DOM树创建完成后(HTML解析的第一步)就触发代码,无需等待页面中其他资源(图片等)的加载。
到此,之前我们实验中遇到的顾虑都被解除了,首先,不用担心代码取不到DOM中想要的元素了,代码不会再失效了;其次,DOM树一创建完成就执行了代码,不用等图片等资源的加载完成了,效率很高;再次,不用人为的加定时器去延迟加载了,也就不用担心定时器的延迟时间拿捏的准不准了。哎,简直不要太开心,这么好的工具,难道不想试着自己造一个吗?
自己模拟一个jQuery的ready方法:
function myReady(fn){ //利用能力检测区分浏览器 if(document.addEventListener){ document.addEventListener("DOMContentLoaded",fn,false); } else{ //这里是IE低版本浏览器 IEContentLoaded(fn); } //IE模拟DOMContentLoaded function IEContentLoaded(fn){ var d = document.window; //保证fn回调函数只执行一次 var done = false; var init = function(){ if(!done){ done = true; fn(); } } (function(){ try{ //DOM树未创建前调用doScroll会抛出异常 d.documentElement.doScroll('left'); } catch(e){ //arguments.callee指向这个即可执行的(function(){})(); //没有捕获异常意味着DOM成功创建,就可以执行回调函数了 setTimeout(arguments.callee,60); return; } })(); //监听document的加载状态 d.onreadystatechange = function(){ if(d.readyState == 'complete'){ d.onreadyState == null; init(); } } } }