jQuery を学習するにはさまざまな方法があります。今日は jQuery の Ready 関数から始めます。この例のコードは、jQuery スクリプト ライブラリからのものです。
jQuery を使用したことがある場合は、ready 関数を使用したことがあるはずです。この関数は、ページの準備ができたときに実行できる関数を登録するために使用されます。
問題は、私たちのページがいつ完成するかということです。
オンロードイベント
最も基本的な処理方法は、ページの 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 イベントは、ページ上の画像などを含むすべてのページ要素が読み込まれるまでトリガーされません。 Web ページに多数の写真がある場合、ユーザーが写真を見る前にページの操作を開始した可能性が考えられます。この時点では、ページは初期化されておらず、イベントも登録されていません。 . それは遅すぎますよね?
DOM の onload イベントに似たよく知られた onload イベントに加えて、標準ベースのブラウザではこのイベントがサポートされており、このイベントはすべての 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(); } }
Diego Perini は 2007 年に、doScroll メソッド呼び出しを使用して、IE がロードされているかどうかを検出する方法を報告しました。詳細な手順については、こちらをご覧ください。
原則として、IE が非 iframe にあるときは、doScroll を実行できるかどうかによって DOM が読み込まれているかどうかを継続的に判断することしかできません。この例では、doScroll を 50 ミリ秒ごとに実行しようとします。ページがロードされていないときに 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() 方法,而之前的那个匿名方法亦被传入其中以备执行。
这种简短写法虽说减少了了一点代码量,但是可读性稍差,所以我个人还是倾向于前面的第一种写法,特别是在团队开发中,仅仅是为了语意明确。