Skip to content

DIY event delegation with jQuery

24 October, 2010

I noticed on a recent project that jQuery’s event delegation methods used jQuery.closest, a method that walks up the DOM to find the closest element of a given type/selector. As useful as it is, IE6 & 7 suck at DOM traversal, and even modern browsers slowly grind to a halt when dealing with massive DOMs. If this is a concern then it’s better to do it yourself.

Below is a simple example of some HTML which has a link (with a child SPAN) and an INPUT – imagine they are deep in the DOM, perhaps in a nested table.  If I use live or delegate to bind events to these elements, then I invoke that event within the context area, jQuery will walk the DOM from the element the event bubbled from to the context element; this could be a traversal of ten or more elements.  Now imagine you have twenty or more event bindings.  The cost of using live or delegate quickly becomes apparent, especially in IE6.

<a href="#" class="specific-link"><span>My link</span></a>
<form action="//myResponsePage">
    <input type="text" />
    <input type="submit" />
</form>

Below is an example of how you can use regular event binding to create your own optimised delegation method. This example is defined as a jQuery plugin. For elements that can not have child elements there is no DOM traversal, for other elements we only ever take three steps up the DOM. The number of steps will depend on your application but I can’t imagine you’d ever want more than five steps.

(function QuickDelegateClosure($){

	var hasNoChildrenRegEx = / input| texarea| select| img/;

	$.fn.quickDelegate = function(selector, event, callback){
		return this.bind(event, _matchSelectorAndInvokeCallback );
		
		function _matchSelectorAndInvokeCallback(event){
			var target, steps = hasNoChildrenRegEx.test(' '+selector)
					  ? 0
					  : 3;
					  
			target = _getClosestWithinNumberOfSteps(event.target,selector,steps);
			if( target ){
				return callback.apply(target,[event]);
			}
		}
	};

	function _getClosestWithinNumberOfSteps(target, selector, steps){
		var count = 0;
		do {
			if ($(target).is(selector)) return target;
		}
		while( (target = target.parentNode) && count++ < steps );
	}
})(jQuery);

And below shows how we might invoke this plugin.

$(function(){
	$('body')
		.quickDelegate('.specific-link', 'click', doSomething )
		.quickDelegate('input[type=text]', 'keypress', doSomething );

	function doSomething(){/*Your code*/}
});

I wrote this pretty quickly, the regular expression it uses to determine if the selector is for an element that has no children is pretty crude. I haven’t formally tested the performance gain of using this compared to using jQuery.delegate though I’ve been impressed with the performance of click and keypress events in IE6 using the same technique. The performance in the application I was working on was notably increased.

Advertisement

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.