Code to add or remove handlers without conflict with legacy code or other project developers.
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.
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:
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);
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.