Event delegation with almost any Selector

Always reapply the events to the DOM objects when they are inserted or loaded? NO MORE!

There are 3 solutions out there at the moment, jQuery listen, jQuery intercept both by event delegation addict Ariel Flesler and LiveQuery a plugin that reapplys all rules to every new DOM element loaded.

(Event delegation = every div.onclick will reach the parents of the div and can be captured there without binding directly to the div)

Listen and intercept both have a very limited selector choise, ‘a.b’ or ‘a#hello’. LiveQuery can be used rather thoughtfree but comes at a load-time-price for every existing/new element.

I like delegation more, but the selector choise is too limited. So here comes my
‘intercept any selector’ hack!

$.intercept(‘table td div.hello a#click :input’,’click’,fn) will bind your Event to the document, and run on every click of ‘table td…’. The lower you bind, the higher the performance cost(needless event checking)
$(‘table’).intercept(‘td div.hello a#click :input’,’click’,fn) will be faster…

As long as the DOM element you bound your events to is not replaced, all events will continue to work. This approach works best for few(and slow) rules that match many elements. There is no initial performance cost, so your page will load fast.

Only when a event is triggered the checking is done, and can be costly if you used a lot of events or a often triggered event (mouseover…).

The code you need to make it happen is here(insert into intercept.js):

  ...
    for( selector in handlers ){
      if( selector == 'self' && e.target == this || $.intercept.matches($target,selector))
        ret = handlers[selector].apply(this, arguments) !== false && ret;
    }
    return ret;
  };

  /**
   * Walk the element backwards and see if the selector matches (before reaching the document)
   * @see https://pragmatig.wordpress.com/2008/03/08/event-delegation-with-almost-any-selector/
   */
  $.intercept.matches = function(obj,selector){
    var root_reached = false;
    var words = selector.split(' ').reverse();
    for(i in words){
    	do{
    	  //match found?
    	  if(obj.is(words[i])){
    		  obj=$(obj.parent());
    		  break;
  	    }
    	  obj=$(obj.parent());
    	  root_reached = obj.get(0) == document;
      }while(!root_reached);
  	  if(root_reached)return false;
    }
    return !root_reached;
  };

2 thoughts on “Event delegation with almost any Selector

  1. Hi Michael

    I’m glad you blog about Intercept. Let me correct one thing from what you said.
    Indeed, Listen supports a reduced version of the selectos engine. But Intercept doesn’t. It support any simple selector as it uses .is() internally, that is, any “filter” selector but no “find” selector.
    You can use any :foo or :foo() filter, etc. On the other hand, Listen can yield much better results, specially on large pages.

    Your post though, suggests a new feature that I could include, the option to take the given selector as “absolute”, taking into account what its position is in the DOM.

    This can be simplified though, by just doing:
    if( $(selector).index(e.target) != -1 )
    ……………;

    I’ll try to add this soon, I’ll add a link to this post.

    Thanks

    ….

    implemented “absolute” selectors in jQuery.Intercept 1.1.2.

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 )

Facebook photo

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

Connecting to %s