Created
March 23, 2009 22:40
-
-
Save colinmollenhour/83824 to your computer and use it in GitHub Desktop.
Event Delegator for Prototype
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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