読者です 読者をやめる 読者になる 読者になる

この日記は私的なものであり所属会社の見解とは無関係です。 GitHub: takahashikzn

addEventListener/attachEventの使い勝手が悪いのでどうにかした

Javascript

addEventListener/attachEventの使い勝手が悪い。


例えばIEのケースを考えると、

node.onchange = function() { ... };
node.attachEvent("onchange", function() { ... });

としたとき、これはどっちか片方しか呼ばれない。

onchangeは一個しか登録できない
(C#みたいに node.onchange += function() { ...} とできればいいのに…)し、
じゃあ全部attachEventでやるか、というのも厳しい。なぜなら、

  1. attachEventした関数を明示的に呼ぶ手段は一応あるが標準化されておらず、実行手順もかなり面倒。
  2. 実行される順番は関数のアタッチ順ではない。

という問題があるから。特に一つ目の問題は、マルチブラウザ対応する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でセットしたハンドラ達は
ビットの海の藻屑となってしまいます。ご注意を。