addEvent() recoding contest entry

Remove border effect.

This implementation is relentlessly platform-neutral. It avoids calling any of the advanced event registration methods at all, though a simple modification could call them when available. Instead it relies only on the old least-common-denominator style of event handling. This code has been tried successfully on Mozilla 1.7, Internet Explorer 5.0 and 6.0, and Safari 2.0.

As usual, if the page unloads with any active event handlers that refer to any part of the DOM tree there will be serious memory leak problems. The code here can be extended to support removal of all handlers on page unload.


// These keep the set of handlers in a property of the object
// named after the event, for example, "click_fns" for onclick
// or "load_fns" for onload.
//
// The addEvent function always installs "callHandlers", which looks
// up the list of handlers and applies each to "this".  If some other
// function is already installed, it includes it in the list.
//
// RemoveEvent deletes from the list and removes the handler function entirely if
// the list becomes empty.


// Portable version of addEvent.  
// 
function addEvent(node, evname, fn) {
  var hname = "on"+evname;
  var aname = evname+"_fns";
  if (node[hname] && node[hname]!=callHandlers) {
    node[aname] = [fn];
  }
  node[hname] = callHandlers;
  node[aname] = node[aname] || new Array;
  var len = node[aname].length;
  node[aname][len] = fn;
}


// Portable version of removeEvent.
//
function removeEvent(node, evname, fn) {
  var hname = "on"+evname;
  var aname = evname+"_fns";
  var a = node[aname];
  if (!a)
    return;
  var a1 = new Array;
  for (var i in a) {
    if (fn!=a[i]) {
      a1[a1.length] = a[i];
    }
  }
  a = node[aname] = a1;
  if (a.length==0) {
    node[hname] = null;
  }
}


// Helper function that calls all the handler functions associated
// with this node and the event's type, treating each one as a method
// of "this".
//
function callHandlers(event) {
  if (!event)
    event = window.event;
  var a = this[event.type+"_fns"];
  var args = [event];
  for (i in a) {
    applyFunc1(a[i], this, args);
  }
}

// Apply a function to a single argument using "apply" or simulating
// it if not available.
// 
function applyFunc1(fn, target, args) {
  if (fn.apply) {
    return fn.apply(target, args);
  } else {
    target.__applyfn = fn;
    var v = target.__applyfn(args[0]);
    // Not in IE 5.0: delete target.__applyfn;
    target.__applyfn = null;
    return v;
  }
}