Google ClosureでDOMContentLoadedイベント
closure のイベント処理には DOMContentLoaded イベントをクロスブラウザで実行してくれる仕組みが用意されていません。
ということでready()関数を作ってみました。
jQueryの $(document).ready(fn); をclosureで書き直しただけです、すいません。。。
ざっくりですけどテストも付けときます。
手元の環境 IE7(Win), FF3.5(Win), Safari4(Mac) で確認してます。
third_party/closure/goog/cheesepie/ready.js
/** * @fileoverview Handle DOMContentLoaded event. * @author http://d.hatena.ne.jp/cheesepie */ goog.provide('goog.events.ready'); goog.require('goog.events'); /** * Handle DOMContentLoaded event. * <pre> * Example: * goog.events.ready(function() { * // some action... * }); * goog.events.listen(document, 'ready', function() { * // some action... * }, false); * </pre> * * @param {function} callback Event listener * @return {function.<goog.events.ready>} */ goog.events.ready = function(fn) { // Attach the listeners goog.events.readyObj.bindReady(); // If element is already ready if (goog.events.readyObj.isReady) { fn.call(document, goog); } else { goog.events.readyObj.readyList.push(fn); } return this; }; /** * The object related goog.events.ready. */ goog.events.readyObj = { /** * @type {boolean} */ isReady: false, /** * @type {Array} */ readyList: [], /** * @type {boolean} */ readyBound: false, /** * bind DOMContentLoaded event. */ bindReady: function() { var ro = goog.events.readyObj; if (ro.readyBound) { return; } ro.readyBound = true; // Mozilla, Opera and webkit if (document.addEventListener) { document.addEventListener('DOMContentLoaded', function() { document.removeEventListener('DOMContentLoaded', arguments.callee, false); ro.ready(); }, false); // If IE event model is used } else if (document.attachEvent) { document.attachEvent('onreadystatechange', function() { if (document.readyState === "complete") { document.detachEvent('onreadystatechange', arguments.callee); ro.ready(); } }); // If IE and not an iframe if (document.documentElement.doScroll && window == window.top) (function() { if (ro.isReady) { return; } try { // If IE us used, use the trick // http://javascript.nwbox.com/IEContentLoaded document.documentElement.doScroll("left"); } catch (error) { setTimeout(arguments.callee, 0); return; } // and execute any waiting functions ro.ready(); })(); } // A failback to window.onload, that will always work goog.events.listenOnce(window, 'load', ro.ready, false); }, /** * Call functions in readyList */ ready: function() { var ro = goog.events.readyObj; if (! ro.isReady) { // Remember that the DOM is ready ro.isReady = true; if (ro.readyList) { // Execute all of them goog.array.forEach(ro.readyList, function(fn, idx) { fn.call(document); }); // Reset the list of functions ro.readyList = null; } goog.events.fireListeners(document, 'ready', false); } } };
third_party/closure/goog/cheesepie/ready_test.html
<!DOCTYPE html> <html> <head> <title>Closure Unit Tests - goog.events.ready</title> <script src="../../../../closure/goog/base.js"></script> <script> goog.require('goog.events'); goog.require('goog.events.ready'); goog.require('goog.testing.jsunit'); </script> </head> <body> <script> function tearDown() { goog.events.removeAll(); } function testOnDOMContentLoaded() { goog.events.ready(function() { assertTrue(goog.events.readyObj.readyBound); }); } function testIsReadyBeforeOnLoad() { window.onload = function() { assertTrue(goog.events.readyObj.isReady); }; } </script> </body> </html>
closureのデモを見ると、bodyの最後にscriptタグを記述して代わりとしているようです。
GoogleのRIAなアプリは、body内を全てJavaScriptで描画するものが多いし、画像もふんだんに使わないから必要ないということなのでしょうか。
Google groupsのディスカッションでもなんで無いの!という意見が出てますね。
https://groups.google.com/group/closure-library-discuss/browse_thread/thread/1beecbb5d6afcb41
確かに速度のこと考えるとbodyの最後に記述するのが一番シンプルで確実なんでしょうけど、そうもいかない案件もあるんですよね。。。
それとgoog.require()便利なんだけど、別のscriptタグで呼ばないといけないとかハマるので、付属のcalcdeps.pyを叩いて1つにまとめてから使う癖を付けといた方がよさそう。