[Code Review] JavaScript Event Binding Script

Hey, was just trying to write a vanilla js solution to event binding. After looking at some examples online I came up with this script.

Some benefits of this:

  • Can be passed either a node or a node list to attach handlers too
  • It checks once to see if the addEventListener and removeEventListener are available and just wraps those if they are.
  • If those methods are not available, it falls back to the Dean Edwards addEvent solution but without the Global pollution.
  • Normalizes the event object passed to the handler a bit so as to ease event delegation and target checks

function BigAB(window, document) {
	this.addEvent = (function( window, document ) {
		if ( document.addEventListener ) {
		return function( elem, type, cb ) {
			if ( (elem && !elem.length) || elem === window ) {
			elem.addEventListener(type, cb, false );
			}
			else if ( elem && elem.length ) {
			var len = elem.length;
			for ( var i = 0; i < len; i++ ) {
				this.addEvent( elem[i], type, cb );
			}
			}
		};
		}
		else if ( document.attachEvent ) {
		return function ( elem, type, cb ) {
			if ( (elem && !elem.length) || elem === window ) {
			if (!cb.$$guid) cb.$$guid = this.addEvent.guid++;
			if (!elem.events) elem.events = {};
			var cbs = elem.events[type];
			if (!cbs) {
				cbs = elem.events[type] = {};
				if (elem['on' + type]) cbs[0] = elem['on' + type];
				elem['on' + type] = handleEvent;
			}
			cbs[cb.$$guid] = cb;
			}
			else if ( elem.length ) {
			var len = elem.length;
			for ( var i = 0; i < len; i++ ) {
				this.addEvent( elem[i], type, cb );
			}
			}
		};
		}
	})( window, document );
	this.addEvent.guid = 1;
	
	this.removeEvent = (function( window, document ) {
		if ( document.removeEventListener ) {
		return function( elem, type, cb ) {
			if ( (elem && !elem.length) || elem === window ) {
			elem.removeEventListener(type, cb, false );
			}
			else if ( elem && elem.length ) {
			var len = elem.length;
			for ( var i = 0; i < len; i++ ) {
				this.removeEvent( elem[i], type, cb );
			}
			}
		};
		}
		else if ( document.detachEvent ) {
		return function ( elem, type, cb ) {
			if ( (elem && !elem.length) || elem === window ) {
			if (elem.removeEventListener) {
				elem.removeEventListener(type, cb, false);
			}
			else if (elem.events && elem.events[type] && cb.$$guid) {
				delete elem.events[type][cb.$$guid];
			}
			}
			else if ( elem.length ) {
			var len = elem.length;
			for ( var i = 0; i < len; i++ ) {
				this.removeEvent( elem[i], type, cb );
			}
			}
		};
		}
	})( window, document );

	function handleEvent(event) {
		event = event || fixEvent(window.event, this);
		var returnValue = true;
		var handlers = this.events[event.type];

		for (var i in handlers) {
		if (!Object.prototype[i]) {
			this.$$handler = handlers[i];
			if (this.$$handler(event) === false) returnValue = false;
		}
		}

		if (this.$$handler) this.$$handler = null;

		return returnValue;
	}

	function fixEvent(event,currentTarget) {
		event.currentTarget = currentTarget;
		event.target = event.srcElement;
		event.preventDefault = fixEvent.preventDefault;
		event.stopPropagation = fixEvent.stopPropagation;
		return event;
	}

	fixEvent.preventDefault = function() {
		this.returnValue = false;
	}

	fixEvent.stopPropagation = function() {
		this.cancelBubble = true;
	}

}

// example usage:
var lib = new BigAB(window,document),
    listItems =  document.getElementsByTagName('li');
	
lib.addEvent(listItems, 'click', function(e) { alert('clicked: '+this.id) });

Any comments or suggestinos would be appreciated.

Overall well done.

Some notes are:

[list][]var declarations should not be scattered throughout each function
[
]Inconsistent indenting can be confusing, as are control statements without braces
[*]What does BigAB stand for, add binding? And what are cbs and cb? Callbacks?
[/list]

Some parts of the code could do with bring broken down in to smaller functions too, for example with the addEvent function:


this.addEvent = (function (window, document) {
    function addW3CEventListener(elem, type, callback) {
        ...
    }
    function addIEEventListener(elem, type, callback) {
        ...
    }
    if (document.addEventListener) {
        return addW3CEventListener;
    } else if (document.attachEvent) {
        return addIEEventListener;
    }
}(window, document));

Before making the above change, it wasn’t clear whether addEvent did some work, or only returned a function for later use.

I’ve also moved the invocation inside of the parenthesis, as it is the function that you are invoking, not the parenthesis themself.

Oh gods, I just noticed. BigAB isn’t add binding, it’s your account name.