Code can be downloaded by right-clicking the following link and selecting save link as or the equivalent menu choice offered by your browser. Download file addevent.js.

Use <script type="text/javascript" src="addevent.js"></script> to add code to your page.

When creating an event handler that will use the Event object and may be bound by assignment to the object's event, it must have the parameter evt to work with our user defined addEvent function. This is needed for the non-IE Event object that is passed as an argument in that situation. The first line of code in the handler should test for evt or window.event to determine which event model is being used by the browser and act accordingly.

The JavaScript file includes the following 6 functions. The 3 with a leading underscore are for use by the other 3 and not intended to be called from user code.

User functions

The three "public" methods are straight forward code. There is a matched pair, addEvent and removeEvent, to carry out the work of adding and removing event handlers. They take three parameters:

obj
A reference to the object, usually an element, where the event handler should be bound.
eventName
The name of the event to receive the handler. Event names are the same as the HTML element attributes for events without the 'on' prefix. E.g., to attach a handler to onclick the event name is click; onmouseover is just mouseover, and so on.
assignment_only
A boolean to indicate that the binding should be by assignment to the object's event. The default is to use addEventListener or attachEvent (and their counters parts removeEventListener and detachEvent).

The third method, removeBinding, is a short cut that attempts to remove a handler from the object's event first, and if the handler was not there, it attempts to use removeEventListener or detachEvent. It get's the same first two arguments as the other functions.

addEvent

function addEvent(obj, event_name, fnc, assigment_only) {
	var rtrn = true;
	if (typeof obj == "undefined") {return false;}

	if (!assigment_only && obj.attachEvent) {
		obj.attachEvent("on"+event_name, fnc);
	}
	else if (!assigment_only && obj.addEventListener) {
		obj.addEventListener(event_name, fnc, false);
	}
	else {
		rtrn = _bindEvent(obj, event_name, fnc);
	}
	return rtrn;
}//eof addEvent

Usage:
addEvent(document.getElementById(id), 'event-name', handler, byAssignment);

removeEvent

function removeEvent(obj, eventName, fnc, assigment_only) {
    var rtrn = true;
    if (typeof obj == "undefined") {return false;}

    if (!assigment_only && obj.detachEvent) {
        obj.detachEvent("on"+eventName, fnc);
    }
    else if (!assigment_only && obj.removeEventListener) {
        obj.removeEventListener(eventName, fnc, false);
    }
    else {
        rtrn = _unbindEvent(obj, eventName, fnc);
    }
    return rtrn;
}//eof removeEvent

Usage:
removeEvent(document.getElementById(id), 'event-name', handler, byAssignment);

removeBinding

function removeBinding(obj, evt, fnc) {
    if (!removeEvent(obj, evt, fnc, true) )
        return removeEvent(obj, evt, fnc, false) ;
}//eof removeBinding

Usage:
removeBinding(document.getElementById(id), 'event-name', handler);

The Internal Functions

There are three "private" functions; albeit, not really private. They have leading underscores and are intended to be called only by the previous functions. These functions do the heavy work of handling handlers that are assigned to the object's event or inline.

_bindEvent

function _bindEvent(obj, event_name, fnc) {
    var str = '', handlers = '';

    /* Extract current handlers */
    if (obj["on" + event_name] != null) {
        str = obj["on" + event_name].toString();     //current event handler wrapper fx
        handlers = _removeWrapper(str, event_name);  //current handlers from without wrapper
    }

    var newListener = fnc.toString();                //new function to string
    var newArray = new Array();
    var rtrnArray = new Array();
    var addReturn = null;

    /* Check if new function has a return statement */
    if (/return \w+;/.test(newListener) ) addReturn = true;

    /* Create function call string to be placed in handler wrapper
     * 1. Create string of passed (new) function declaration line
     *    converting to function call, with optional return:
     *     e.g. function x() {etc.} becomes x(); or return x();
     * 2. append to string of existing event handlers putting
     *    any with return value at end of list.
     */
    newListener = (addReturn ? 'return ' : '') + newListener.match(/function ([\w$]*\s*\([\w]*\))\s*\{?/)[1];
    handlers += '\n' + newListener + ';';

    /* Put function calls into 1 of 2 arrays
     * based on whether the function returns a value
     */
    var handlerArray = handlers.match(/[^;]+;\s*/g); //returns array from string separated at /[^;]+;\s*/

    for (var i = 0; i < handlerArray.length; i++) {
        if (!/return /.test(handlerArray[i]) ) {
            newArray[newArray.length] = handlerArray[i].replace(/^\s*|\s*$/g, '');
        }
        else {
            rtrnArray[rtrnArray.length] = handlerArray[i].replace(/^\s*|\s*$/g, '');
        }
    }

    /* Combine the function calls into a string
     * with those returning a value last
     */
    handlers = newArray.join('\n');
    if (rtrnArray.length > 0) handlers  += '\n' + rtrnArray.join('\n');

    /* Bind handlers back to object's event*/
    obj["on" + event_name] = new Function("evt", handlers);
    return true;
}//eof _bindEvent

Usage:
none.

_unbindEvent

function _unbindEvent(obj, eventName, fnc) {
    if (obj["on" + eventName] != null)
        var eventHandler = obj["on" + eventName].toString();
    else
        return false;

    var eventListeners = _removeWrapper(eventHandler, eventName);

    /* Get call line for fx being removed */
    fnc = fnc.toString();

    var fx_name = fnc.match(/function ([\w$])*\s*\([\w]*\)\s*\{?/)[1];
    var re = new RegExp(fx_name);

    /* split event listeners into array & create array for kept listeners */
    var fxArray = eventListeners.match(/[^;]+;\s*/g);
    var newArray = new Array();

    /* Copy listeners being retained to newArray skipping one being deleted*/
    var looking = true;   //so only one entry is removed
    fxArray.reverse();    //take last entry first
    for (var i = 0; i < fxArray.length; i++) {
        if (looking && re.test(fxArray[i]))
            looking = false;
        else
            newArray[newArray.length] = fxArray[i].replace(/^\s*|\s*$/g, '');
    }

    /* Bind kept event listeners to event */
    if (!looking) {
        if (newArray.length > 0) {
            newArray.reverse(); //Back to original order
            eventListeners = newArray.join('\n');
            obj["on" + eventName] = new Function("evt", eventListeners);
        }
        else {
            obj["on" + eventName] = null;
        }
    }
    return !looking;
}//eof _unbindEvent

Usage:
none

_removeWrapper

function _removeWrapper(eventHdlrStr, eName) {
    /* Remove handler wrapper for function calls using RegExp*/
    var re1 = /^\s*function anonymous\([\w]*\)\s*\{\s*/;
    var re2 = new RegExp("function on" + eName + "\\(\\w*\\)\\s*\\{");
    var re3 = /^\s*function\s+\(\w*\)\s*\{\s*/;

    /* Remove handlerr wrapper */
    if (re1.test(eventHdlrStr) )
        eventHdlrStr = eventHdlrStr.replace(re1, "");        //remove anonymous fx declaration IE and this
    else if (re2.test(eventHdlrStr))
        eventHdlrStr = eventHdlrStr.replace(re2, "");        //remove onevent fx declaration Mozilla
    else if (re3.test(eventHdlrStr) )
        eventHdlrStr = eventHdlrStr.replace(re3, "");        //remove KHTML anonymous (no name);
    else {
        var addReturn = /return \w+/.test(eventHdlrStr);
        eventHdlrStr.match(/function ([\w$]*\s*\([\w]*\))\s*\{?/);
        eventHdlrStr = ((addReturn) ? "return " : "") + RegExp.$1 + ';';
    }

    eventHdlrStr = eventHdlrStr.replace(/\s*\}\s*$/, '');    //remove closing brace

    /* Cleanup remaining function calls */
    eventHdlrStr = eventHdlrStr.replace(/^\s*|\s*$/g, '');     //all trim
    eventHdlrStr = eventHdlrStr.replace(/\s+/g, ' ');          //Replace multiple white spaces with one space
    eventHdlrStr = eventHdlrStr.replace(/\bevent\b/g, 'evt');  //replace key word event with local evt
    if (! /;$/.test(eventHdlrStr)) {eventHdlrStr += ";";}      //add closing semicolon

    /* create array of function calls based on semicolon */
    var handlerArray = eventHdlrStr.match(/[^;]+;\s*/g);

    /* trim each function call */
    if (handlerArray) {
        for (var i = 0; handlerArray && i < handlerArray.length; i++) {
            handlerArray[i] = handlerArray[i].replace(/^\s*|\s*$/g, '');
        }
        return handlerArray.join('\n');
    }
    else {
        return '';
    }
}//eof _removeWrapper

Usage:
None.

The methods _bindEvent and _unbindEvent do the work of getting the handlers asigned to the object's event into a string, editing the string to add or remove a handler and assigning the remaining handlers back to the event wrapped in an anonymous function.

_removeWrapper does the specific task of removing the wrapper function. As was discussed in the Introduction, inline events are wrapped in a function by the browser, and this is the only way multiple handlers can be bound to an event using assignment. There are three RegExps to identify the wrapper because browsers use different function declarations. We also have to avoid removing the function declaration when a single handler is assigned to the event without a wrapper function.