IEでイベントの順番がおかしくなる訳

ページ表示を待って処理を実施したいときに Prototype の Event.observe(window, 'load', function(){...}); をよく使います。 これを複数回使った場合、登録された順に実行される… はずだったのですが、IE では順番が狂うみたい。 んでさっそく、テストページを作ってみました。


実行する JavaScript は以下のように単純なものです。

Event.observe(window, 'load', function(){ alert(1); });
Event.observe(window, 'load', function(){ alert(2); });

実行結果

以下は定義した順 (1,2) で表示されたもの。


で、以下が定義した逆順 (2,1) で表示されたもの。

  • IE8 Beta
  • IE7
  • IE6 SP2


実行結果はやはりといいますか、IE だけが変でした。 標準準拠(xhtml)モード、互換(HTML)モードでは結果はかわりませんでした。

IE だけ結果が変な理由

Prototype 1.6.0.2 の該当部分のコードは以下のようになっています。 まぁ、一般的な記述でしょう。

if (element.addEventListener) {
  element.addEventListener(name, wrapper, false);
} else {
  element.attachEvent("on" + name, wrapper);
}

IE では addEventListener が使えなくて、かわりに attachEvent があるのですが、この実装が問題。 登録した順には実行してず、逆順ですらなく… なんと順不同で呼び出してくれちゃう模様。 なんじゃそりゃ。

この問題、IE8βでも直っていなかったのが、個人的にはプチショックです。 DOM に準拠して、そろそろ addEventListener が実装されてると予想していたから。 それなら attachEvent がこれまで順不同だったので、せめて登録順の実装に換えといてくれよぉ、とか呟いてみたり。

とりあえずの逃げ

自分的に最も困るのが、window の onLoad イベントで、こればかりは登録順であって欲しい。 他のは順不同でも呼ばれないわけじゃないから、まぁなんとかなります。

具体的には、自分の Web で使用させてもらっている GlassBox というライブラリに非常に癖がありまして… 初期化の順番を違えると表示がうまくできないんだ、これが。

で、クイックハックといいますか、いちばんベースのライブラリに以下のコードを追加しています。

jp.rinco.observeList = [];
Event.observe(window, 'load', function(){
  jp.rinco.observeList.each(function(f){
    if (f && f instanceof Function)
      f();
    });
});

利用するほうでは、以下のように記載します。 将来的に廃止できることを期待して、条件判断かませています。

if (jp.rinco.observeList)
  jp.rinco.observeList.push(myInit);
else
  Event.observe(window, 'load', myInit);

やってることは単純で、自分で関数リストを管理して、順に実行しちゃう訳ですね。 単なる配列ですから、処理の最初に放り込んだり自由にできます。 今のところ特にシビアな処理は呼ばないので、エラーチェックはサボっています。