Events are one aspect of the Document Object Model (DOM) that are highly inconsistent across browsers. Back in the early days of the Web, Netscape introduced one model of attaching event listeners to the document. Unsurprisingly, Microsoft chose to implement events in a completely different way in Internet Explorer. When the W3C came along to standardize the process, this only served to muddy the waters even further by introducing a third approach.
Handling Browser Event Inconsistencies
Without a toolkit, the developer is left to work around these inconsistencies themselves. Event handling code without a JavaScript toolkit tends to look something like this:
/* written by Dean Edwards, 2005 with input from Tino Zijdel - crisp@xs4all.nl http://dean.edwards.name/weblog/2005/10/add-event/ */ function addEvent(element, type, handler) { if (element.addEventListener) { element.addEventListener(type, handler, false); } else { if (!handler.$$guid) handler.$$guid = addEvent.guid++; if (!element.events) element.events = {}; var handlers = element.events[type]; if (!handlers) { handlers = element.events[type] = {}; if (element['on' + type]) handlers[0] = element['on' + type]; element['on' + type] = handleEvent; } handlers[handler.$$guid] = handler; } } addEvent.guid = 1; function removeEvent(element, type, handler) { if (element.removeEventListener) { element.removeEventListener(type, handler, false); } else if {(element.events && element.events[type] && handler.$$guid) delete element.events[type][handler.$$guid]; } } function handleEvent(event) { event = event || fixEvent(window.event); 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) { event.preventDefault = fixEvent.preventDefault; event.stopPropagation = fixEvent.stopPropagation; return event; } fixEvent.preventDefault = function() { this.returnValue = false; } fixEvent.stopPropagation = function() { this.cancelBubble = true; }
Binding Events
Assuming we have markup like this...
<ol id="veggies"> <li>Carrots</li> <li>Zucchini</li> <li>Squash</li> </ol>
...and we want to bind focus handlers to each item in the list, the code using our hand-rolled toolkit would look something like this:
var listOfVeggies = document.getElementById("veggies"); var veggieNodes = listOfVeggies.getElementsByTagName("li"); for (var i = 0; i < veggieNodes.length; i++) { addEvent(veggieNodes[i], "onclick", function (evt) { eatVegetable(evt.target); }); }
jQuery provides a simple and succinct mechanism for attaching events to elements. Since we're reusing an existing toolkit, we can get rid of all the event handler workaround code from the first example. The jQuery code to actually bind event handlers to each item in the list looks like this:
jQuery("#veggies > li").focus(function (evt) { eatVegetable(evt.target); });
Just counting the code to actually bind the event handlers, that's a line-for-line savings of nearly 70%, and the latter form is significantly more readable.