jQuery를 배우는 방법은 다양합니다. 오늘은 jQuery의 Ready 함수부터 시작하겠습니다. 이 예제의 코드는 jQuery 스크립트 라이브러리에서 가져온 것입니다.
jQuery를 사용하셨다면 페이지가 준비되었을 때 실행할 수 있는 함수를 등록하는 데 사용되는 준비 함수를 사용하셨을 것입니다.
문제는 우리 페이지가 언제 준비되느냐는 것입니다.
온로드 이벤트
가장 기본적인 처리 방법은 페이지의 onload 이벤트입니다. 이 이벤트를 처리할 때 HTML을 통해 body 요소의 시작 태그에 직접 작성하는 방법이 있습니다. 사용하는 이벤트 등록 방법으로 DOM0 방식과 DOM2 방식으로 나눌 수 있습니다. 브라우저 호환성을 고려하여 아래와 같이 DOM2 모드로 작성합니다.
if (document.addEventListener) { // A fallback to window.onload, that will always work window.addEventListener("load", jQuery.ready, false); // If IE event model is used } else { // A fallback to window.onload, that will always work window.attachEvent("onload", jQuery.ready); }
DOMContentLoaded 이벤트
그러나 onload 이벤트는 페이지의 이미지 등을 포함한 모든 페이지 요소가 로드될 때까지 트리거되지 않습니다. 웹페이지에 사진이 많으면 그 효과를 짐작할 수 있습니다. 현재로서는 페이지가 초기화되지 않았고 이벤트도 등록되지 않은 상태입니다. . 너무 늦지 않았나요?
DOM의 onload 이벤트와 유사한 잘 알려진 onload 이벤트 외에도 고려해야 할 DOMContentLoaded 이벤트가 있습니다. 이 이벤트는 모든 DOM이 구문 분석될 때 트리거됩니다. .
이런 방식으로 표준 기반 브라우저의 경우 이 이벤트 처리를 등록할 수도 있습니다. 이러한 방식으로 로딩 완료 이벤트를 더 일찍 포착할 수 있습니다.
if (document.addEventListener) { // Use the handy event callback document.addEventListener("DOMContentLoaded", DOMContentLoaded, false); // A fallback to window.onload, that will always work window.addEventListener("load", jQuery.ready, false); }
onreadystatechange 이벤트
비표준 브라우저는 어떻게 해야 하나요?
브라우저에 document.onreadystatechange 이벤트가 있는 경우 이벤트가 발생하면 document.readyState=complete이면 DOM 트리가 로드된 것으로 간주할 수 있습니다.
그러나 이 이벤트는 그다지 안정적이지 않습니다. 예를 들어 페이지에 이미지가 있는 경우 onload 이벤트가 끝날 때까지 트리거되지 않을 수 있습니다. 즉, 페이지에 바이너리가 없는 경우에만 올바르게 실행될 수 있습니다. 리소스가 매우 적거나 캐시됩니다.
if (document.addEventListener) { // Use the handy event callback document.addEventListener("DOMContentLoaded", DOMContentLoaded, false); // A fallback to window.onload, that will always work window.addEventListener("load", jQuery.ready, false); // If IE event model is used } else { // Ensure firing before onload, maybe late but safe also for iframes document.attachEvent("onreadystatechange", DOMContentLoaded); // A fallback to window.onload, that will always work window.attachEvent("onload", jQuery.ready); }
DOMContentLoaded 함수는 무엇을 합니까? 마지막으로 jQuery.ready 함수를 호출해야 합니다.
DOMContentLoaded = function() { if ( document.addEventListener ) { document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false ); jQuery.ready(); } else if ( document.readyState === "complete" ) { // we're here because readyState === "complete" in oldIE // which is good enough for us to call the dom ready! document.detachEvent( "onreadystatechange", DOMContentLoaded ); jQuery.ready(); } }
2007년 Diego Perini는 doScroll 메서드 호출을 사용하여 IE가 로드되었는지 확인하는 방법을 보고했습니다. 자세한 지침은 여기에서 확인할 수 있습니다.
원칙은 IE가 iframe이 아닌 곳에 있을 때 doScroll을 실행할 수 있는지 여부에 의해서만 DOM이 로드되었는지 지속적으로 판단할 수 있다는 것입니다. 이 예에서는 50밀리초마다 doScroll을 실행해 봅니다. 페이지가 로드되지 않을 때 doScroll을 호출하면 예외가 발생하므로 try -catch를 사용하여 예외를 포착합니다.
(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); } // and execute any waiting functions jQuery.ready(); } })();
document.readyState 状态
如果我们注册 ready 函数的时间点太晚了,页面已经加载完成之后,我们才注册自己的 ready 函数,那就用不着上面的层层检查了,直接看看当前页面的 readyState 就可以了,如果已经是 complete ,那就可以直接执行我们准备注册的 ready 函数了。不过 ChrisS 报告了一个很特别的错误情况,我们需要延迟一下执行。
setTimeout 经常被用来做网页上的定时器,允许为它指定一个毫秒数作为间隔执行的时间。当被启动的程序需要在非常短的时间内运行,我们就会给她指定一个很小的时间数,或者需要马上执行的话,我们甚至把这个毫秒数设置为0,但事实上,setTimeout有一个最小执行时间,当指定的时间小于该时间时,浏览器会用最小允许的时间作为setTimeout的时间间隔,也就是说即使我们把setTimeout的毫秒数设置为0,被调用的程序也没有马上启动。
这个最小的时间间隔是多少呢?这和浏览器及操作系统有关。在John Resig的新书《Javascript忍者的秘密》一书中提到
Browsers all have a 10ms minimum delay on OSX and a(approximately) 15ms delay on Windows.(在苹果机上的最小时间间隔是10毫秒,在Windows系统上的最小时间间隔大约是15毫秒)
,另外,MDC中关于setTimeout的介绍中也提到,Firefox中定义的最小时间间隔(DOM_MIN_TIMEOUT_VALUE)是10毫秒,HTML5定义的最小时间间隔是4毫秒。既然规范都是这样写的,那看来使用setTimeout是没办法再把这个最小时间间隔缩短了。
这样,通过设置为 1, 我们可以让程序在浏览器支持的最小时间间隔之后执行了。
// 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") { // 延迟 1 毫秒之后,执行 ready 函数 setTimeout(jQuery.ready, 1); }
完整的代码
在 jQuery 中完整的代码如下所示。位于 jQuery 1.8.3 源代码的 #842 行。
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, 1 ); // Standards-based browsers support DOMContentLoaded } else if ( document.addEventListener ) { // Use the handy event callback document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false ); // A fallback to window.onload, that will always work window.addEventListener( "load", jQuery.ready, false ); // If IE event model is used } else { // Ensure firing before onload, maybe late but safe also for iframes document.attachEvent( "onreadystatechange", DOMContentLoaded ); // A fallback to window.onload, that will always work window.attachEvent( "onload", jQuery.ready ); // 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 ); } // and execute any waiting functions jQuery.ready(); } })(); } } } return readyList.promise( obj ); };
那么,又是谁来调用呢?当然是需要的时候,在我们调用 ready 函数的时候,才需要注册这些判断页面是否完全加载的处理,这段代码在 1.8.3 中位于代码的 #244 行,如下所示:
ready: function( fn ) { // Add the callback jQuery.ready.promise().done( fn ); return this; }
在页面上引用 jQuery 脚本库之后,执行了 jQuery 的初始化函数,初始化函数中创建了 ready 函数。我们在通过 ready 函数注册事件处理之前,jQuery 完成了页面检测代码的注册。这样。当页面完全加载之后,我们注册的函数就被调用了。
jQuery Ready 方法的简短写法
写 jQuery 代码的时候,一般要写一个 Ready 方法,以确保 DOM 已加载完毕,然后再执行相应的 jQuery 代码。Ready 方法一般写法如下:
$(document).ready(function() { // 从这里开始 });
但是在看其他人写的 jQuery 代码的时候,经常又会看到如下写法:
$(function() { // 从这里开始 });
第二种写法虽然简短了许多,但是在功能上和第一种写法是等价的,如果你不相信,可以看一下 jQuery 的源代码中有如下代码片段:
// HANDLE: $(function) // Shortcut for document ready if ( jQuery.isFunction( selector ) ) { return rootjQuery.ready( selector ); }
如果传入选择器中的参数是一个函数,那么会自动返回一个 rootjQuery.ready( selector ),而 rootjQuery 又是 jQuery(document) 的一个引用,所以这里就相当于调用 jQuery(document).ready() 方法,而之前的那个匿名方法亦被传入其中以备执行。
这种简短写法虽说减少了了一点代码量,但是可读性稍差,所以我个人还是倾向于前面的第一种写法,特别是在团队开发中,仅仅是为了语意明确。