addEventListener/attachEventの使い勝手が悪い。
例えばIEのケースを考えると、
node.onchange = function() { ... }; node.attachEvent("onchange", function() { ... });
としたとき、これはどっちか片方しか呼ばれない。
onchangeは一個しか登録できない
(C#みたいに node.onchange += function() { ...}
とできればいいのに…)し、
じゃあ全部attachEventでやるか、というのも厳しい。なぜなら、
- attachEventした関数を明示的に呼ぶ手段は一応あるが標準化されておらず、実行手順もかなり面倒。
- 実行される順番は関数のアタッチ順ではない。
という問題があるから。特に一つ目の問題は、マルチブラウザ対応するUIコンポーネントを作るときは致命的。
というわけで作った
addEventListener/attachEventの問題点を全部解消した関数を作った。
まあprototypeとかjQueryとかに、似たような機能はあるだろうけど
勉強も兼ねて自作ということで。
これを使えば、
- attachEventした関数を明示的に呼ぶには、単に
node.onchange();
などとすればよい。ラクチン。 - 実行される順番は関数のアタッチ順。
が実現できる。
/* void */ function addEventListenerEx( /* Node */ node, /* String */ eventName, /* Function */ listener) { if (eventName == null) { throw new Error("eventName is null"); } else if (listener == null) { throw new Error("listener is null"); } /* String */ var listenersName = "__listeners__"; /* String */ var handlerName = "on" + eventName; // 専用関数未セット if (node[handlerName] == null) { node[handlerName] = /* void */ function() { for (/* int */ var i = 0; i < this[handlerName][listenersName].length; i++) { this[handlerName][listenersName][i](); } }; node[handlerName][listenersName] = [ listener ]; } // 通常の関数がセット済み else if (node[handlerName][listenersName] == null) { /* Function */ var oldHandler = node[handlerName]; node[handlerName] = /* void */ function() { for (/* int */ var i = 0; i < this[handlerName][listenersName].length; i++) { this[handlerName][listenersName][i](); } }; node[handlerName][listenersName] = [ oldHandler, listener ]; } // 専用関数がセット済み else { node[handlerName][listenersName].push(listener); } };
制限事項
addEventListeneExは、ノードのイベントハンドラスロットに関数をセットする。
なので、
addEventListenerEx(node, "change", function() { ... });
とした後で、
node.onchange = function() { ... };
としてしまうと、addEventListenerExでセットしたハンドラ達は
ビットの海の藻屑となってしまいます。ご注意を。