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つにまとめてから使う癖を付けといた方がよさそう。