我们都知道javascript中的window.onload方法的不足:必须等到所有图片和富文本媒体加载完后才能执行,影响用户体验。更好的做法是等到dom加载完即执行相应回调。类似jQuery中domReady方法应运而生。
在jQuery中用这个方法非常简便:$(function(){}),这个其实是$(document).ready(function(){})的简便写法,见到源码:
rootjQuery = jQuery(document); if ( jQuery.isFunction( selector ) ) { return rootjQuery.ready( selector ); }
在简单的背后,是jQuery进行了封装,屏蔽了很多兼容性问题。我们来看看原生的domReady事件,了解下有那些坑。
1,w3c的DOMContentLoaded,想说爱你不容易。
本来是个好方法,奈何对于IE6,7,8,只能呵呵了。
2,setTimeout
setTimeout设置的函数, 会在readyState为complete时触发, 但是触发时间点是在图片资源加载完毕后.
3,readyState
readyState为interactive时, DOM结构并没有稳定, 此时依然会有脚本修改DOM元素, readyState为complete时, 图片已经加载完毕
4,外部script(通过设置script的defer属性实现)
外部script:如果将此script放在页面上方, 则无法稳定触发. 并且触发时DOM结构依然可能发生变化.
5,doScroll(微软的文档只出doScroll必须在DOM主文档准备完毕时才可以正常触发. 所以通过doScroll判断DOM是否准备完毕)
doScroll通过时readyState可能为interactive, 也可能为complete. 但是一定会在DOM结构稳定后, 图片加载完毕前执行
方法很多,比较杂乱。jQuery的方法还是比较可取的,监听各种触发条件。宁可错杀一千,绝不放过一个。
来看看源码的实现:
ready: function( fn ) { // Add the callback jQuery.ready.promise().done( fn ); return this; },
jQuery.ready.promise是个异步的Deffered对象,完成后触发回调fn。
Deferred对象什么时候完成呢?最好的结果当然是dom加载完后马上触发。这个时候就是各种监听方法。
jQuery.ready.promise = function( obj ) { if ( !readyList ) { readyList = jQuery.Deferred(); // 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 if ( document.readyState === "complete" ) { // Handle it asynchronously to allow scripts the opportunity to delay ready setTimeout( jQuery.ready ); // Standards-based browsers support DOMContentLoaded } else if ( document.addEventListener ) { // Use the handy event callback document.addEventListener( "DOMContentLoaded", completed, false ); // A fallback to window.onload, that will always work window.addEventListener( "load", completed, false ); // If IE event model is used } else { // Ensure firing before onload, maybe late but safe also for iframes 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 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(); } })(); } } } return readyList.promise( obj ); };
多少判断,多少绑定......首先是判断document.readyState。如果是等于complete,立即执行回到
document.readyState === "complete"
根据浏览器的不同,分别绑定:
标准浏览器绑定:DOMContentLoaded,绑定load
document.addEventListener("DOMContentLoaded", completed, false)
window.addEventListener("load", completed, false)
IE浏览器下
document.attachEvent("onReadyStateChange", completed)
window.attachEvent("load", completed)
根据top.doScroll("left")判断是否加载完,加载完后直接触发:jQuery.ready();
再来看看completed方法
completed = function( event ) { // readyState === "complete" is good enough for us to call the dom ready in oldIE if ( document.addEventListener || event.type === "load" || document.readyState === "complete" ) { detach(); jQuery.ready(); } },
还是触发jQuery.ready。最后来看看jQuery.ready
ready: function( wait ) { // Abort if there are pending holds or we‘re already ready if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { return; } // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). if ( !document.body ) { return setTimeout( jQuery.ready ); } // 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.resolveWith( document, [ jQuery ] ); // Trigger any bound ready events if ( jQuery.fn.trigger ) { jQuery( document ).trigger("ready").off("ready"); } },
最后还是要触发回调:readyList.resolveWith( document, [ jQuery ] );
这个就是jQuery中domReady的实现。还是比较完美的。
优点是:加入了异步回调之后结构非常优美,想在哪里触发就在哪里触发。而且几乎全兼容。
缺点就是监听方法太多,对兼容性要求不高,不用这么费性能的方法。
参考文档:http://www.cnblogs.com/zhangziqiu/archive/2011/06/27/DOMReady.html