前作った仕組みだと無名関数の特定ができていないので修正しました。

よく見れば、前回作ったときの

1
var id = "f_" + type + "_" + callback;

これでは異なる要素の同じイベントハンドラを区別できないです。

というわけで、addListenerの時に要素・イベントタイプ・コールバック・コールバック呼出元と、これらから作成した無名関数もまとめてスタックしておき、removeListenerの時に要素・イベントタイプ・コールバック・コールバック呼出元が全て一致したとき、スタックから取り出した無名関数を返すことにした。

removeListenerの時に同じ素材で新たに無名関数を作っても、比較したときに異なるオブジェクトとみなされてしまうので、最初に作成した変数を参照で(のFunctionは参照渡しらしい)呼び出す。この辺、前回時は理解できてなかった。

コールバックに無名関数を使いたい時もあるので、callbackが関数型の場合はスタックに積まずにリスナー登録のみ。これをremoveするには無名関数内でarguements.callee。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
EventHandler = {
  addListener: function(element, type, callback, context) {
    var func;
    if (typeof(callback) == "function") {
      func = callback;
    } else if(typeof(callback) == "string") {
      func = function(e){ context[callback](e); };
      EventHandler.Stack.addStack(element, type, callback, context, func);
    } else {
      return;
    }
    if(element.addEventListener) element.addEventListener(type, func, false);
    else if(element.attachEvent) element.attachEvent("on"+type, func);
  },
  removeListener: function(element, type, callback, context) {
    if (!typeof(callback) == "string") return;
    var num = EventHandler.Stack.findStack(element, type, callback, context);
    var func = EventHandler.Stack.getStack(num).func;
    EventHandler.Stack.removeStack(num);

    if (func == null) return;
    if(element.removeEventListener) element.removeEventListener(type, func, false);
    else if(element.detachEvent) element.detachEvent("on" + type, func);
  }
}

こちらはスタックを保持するためのオブジェクト

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
EventHandler.Stack = {
  _count: 0,
  _list: [],
  addStack: function(element, type, callback, context, func) {
    var obj = {
      id: EventHandler.Stack.getUID(),
      element: element,
      type: type,
      callback: callback,
      context: context,
      func: func
    };
    EventHandler.Stack._list.push(obj);
  },
  removeStack: function(num) {
    EventHandler.Stack._list.splice(num, 1);
  },
  getStack: function(num) {
    return EventHandler.Stack._list[num];
  },
  findStack: function(e, t, cb, ct) {
    for (var i = 0; i < EventHandler.Stack._list.length; i++) {
      var s = EventHandler.Stack._list[i];
      if (s.element == e && s.type == t && s.callback == cb && s.context == ct) {
          return i;
      }
    }
    return null;
  },
  getUID: function() {
      return EventHandler.Stack._count++;
  }
}

いちお、これでFF、Safari、IEで動いているけど、どうなんでしょう。僕的にはとても便利です。