Skip to content

Instantly share code, notes, and snippets.

@colinmollenhour
Created March 23, 2009 22:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save colinmollenhour/83824 to your computer and use it in GitHub Desktop.
Save colinmollenhour/83824 to your computer and use it in GitHub Desktop.
Event Delegator for Prototype
/*
Author: Colin Mollenhour
Delegator - An event delegation class for Prototype.
Supports CSS selectors or binary functions and mouseenter and mouseleave emulation.
Usage:
1. Create a 'Delegator' instance passing the event type to delegate. You cannot change the event type later.
2. Add some rules to the Delegator instance using "add". Rules can be added or removed at any point.
3. Observe an element using "observe". Multiple elements can be observed by the same
Delegator instance with repeated calls to "observe".
API:
All functions are chainable to be concise.
add(rule, [rule, [rule, ...]])
Add one or more rules. Each rule is an array of [test,action].
The 'test' can be a CSS selector, or a function that returns true/false.
The 'action' function will get called if the test is successful or the selector matches.
It's arguments will be the event just as if the handler had been called directly by the event listener.
remove(rule, [rule, [rule, ...]])
Remove one or more rules that were added with "add". Use "dispose" instead if you want to cleanup the entire Delegator instance.
observe([element])
Observe the element (or the document element if no element passed). This will effectively start the event delegation for that element.
stopObserving([element])
Stop observing the element (or the document element if no element passed). This will effectively stop event delegation for that element.
dispose()
Cleanup the entire Delegator instance. All observed elements will no longer be observed and the rules will be cleared.
Sample Usage:
var clickDelegator = new Delegator('click').add(
['li.restricted', function(event){
event.stop(); //stop the actual click event
alert('You are not allowed to visit '+event.element().innerHTML);
return false;
}]
).observe();
//function to check if an element id matches a regex
Element.testId = function(element,regex){
return element.id && element.id.match(regex);
};
//add some additional rules
clickDelegator.add(
[Element.testId.curry(/^page-\d+$/), function(event){
new Ajax.Updater(url,container,{parameters: {id: this.id}});
}]
);
//later... cleanup
clickDelegator.dispose();
*/
var Delegator = Class.create({
initialize: function(type){
this.type = type;
this.rules = [];
this.handlers = {};
},
add: function(){
this.rules = this.rules.concat($A(arguments));
return this;
},
remove: function(){
for(var j=0; j < arguments.length; ++j){
var arg = arguments[j];
for(var i=0, len=this.rules.length; i < len; ++i){
var rule = this.rules[i];
if(rule == arg || (rule[0] == arg[0] && rule[1] == arg[1])){
this.rules.splice(i,1);
break;
}
}
}
return this;
},
getEventType: function(){
if(this.type == 'mouseenter') return 'mouseover';
if(this.type == 'mouseleave') return 'mouseout';
return this.type;
},
observe: function(element){
element = $(element || document.documentElement);
this.handlers[element] = this._go.bindAsEventListener(this,element);
Event.observe(element,this.getEventType(),this.handlers[element]);
return this;
},
stopObserving: function(element){
element = $(element || document.documentElement);
Event.stopObserving(element,this.getEventType(),this.handlers[element]);
delete this.handlers[element];
return this;
},
dispose: function(){
for(var element in this.handlers)
this.stopObserving(element);
this.rules = [], this.handlers = {};
},
_go: function(event,parent){
if(!event.target) return; /* IE will have event.srcElement == null on some mouseout events */
var element = $(event.element()), related = event.relatedTarget;
for(var i=0, len=this.rules.length; i < len; ++i){
var rule = this.rules[i], current = element;
if( Object.isString(rule[0]) ){
do{
if(!$(current).match(rule[0])) continue;
if( (this.type == 'mouseenter' || this.type == 'mouseleave') &&
(!related || related == current || related.childOf(current))
) continue; //simulate mouseenter/mouseleave
if(rule[1].call(current,event) === false) return;
}while( current != parent && (current = current.parentNode) );
}else if( Object.isFunction(rule[0]) ){
do{
if(!rule[0](event,current)) continue;
if( (this.type == 'mouseenter' || this.type == 'mouseleave') &&
(!related || related == current || related.childOf(current))
) continue; //simulate mouseenter/mouseleave
if(rule[1].call(current,event) === false) return;
}while( current != parent && (current = current.parentNode) );
}
}
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment