delegate.js revision 63d012ee193ba8c768b2a2aade99081422759213
3f3aa287185afb5d48d7ef0717054a154c372dc9Adam Moore * Adds event delegation support to the library.
3f3aa287185afb5d48d7ef0717054a154c372dc9Adam Moore * @module event
3f3aa287185afb5d48d7ef0717054a154c372dc9Adam Moore * @submodule event-delegate
73cf5b20418beae941f34ec39a8d87035ae01711Luke Smithvar toArray = Y.Array,
680f13616a493c7bf3a794982e07d10abd9763b3Luke Smith * <p>Sets up event delegation on a container element. The delegated event
680f13616a493c7bf3a794982e07d10abd9763b3Luke Smith * will use a supplied selector or filtering function to test if the event
680f13616a493c7bf3a794982e07d10abd9763b3Luke Smith * references at least one node that should trigger the subscription callback.
680f13616a493c7bf3a794982e07d10abd9763b3Luke Smith * Selector string filters will trigger the callback if the event originated
680f13616a493c7bf3a794982e07d10abd9763b3Luke Smith * from a node that matches it or is contained in a node that matches it.
680f13616a493c7bf3a794982e07d10abd9763b3Luke Smith * Function filters accept the unfiltered event object and should return null
680f13616a493c7bf3a794982e07d10abd9763b3Luke Smith * if it does not qualify for firing the callbacks, a Node if the callback
680f13616a493c7bf3a794982e07d10abd9763b3Luke Smith * should be executed for one Node, or an array of Nodes if there were multiple
680f13616a493c7bf3a794982e07d10abd9763b3Luke Smith * nodes in touched by the event that meet the filtering criteria.</p>
680f13616a493c7bf3a794982e07d10abd9763b3Luke Smith * <p>The callback function will be executed only for those cases where the
680f13616a493c7bf3a794982e07d10abd9763b3Luke Smith * filter returns at least one match. For each matching Node, the callback
680f13616a493c7bf3a794982e07d10abd9763b3Luke Smith * will be executed with its 'this' object set to the Node matched by the
680f13616a493c7bf3a794982e07d10abd9763b3Luke Smith * filter (unless a specific context was provided during subscription), and the
680f13616a493c7bf3a794982e07d10abd9763b3Luke Smith * provided event's <code>currentTarget</code> will also be set to the matching
680f13616a493c7bf3a794982e07d10abd9763b3Luke Smith * Node. The containing Node from which the subscription was originally made
680f13616a493c7bf3a794982e07d10abd9763b3Luke Smith * can be referenced as <code>e.container</code>.
680f13616a493c7bf3a794982e07d10abd9763b3Luke Smith * @method delegate
680f13616a493c7bf3a794982e07d10abd9763b3Luke Smith * @param type {String} the event type to delegate
680f13616a493c7bf3a794982e07d10abd9763b3Luke Smith * @param fn {Function} the callback function to execute. This function
680f13616a493c7bf3a794982e07d10abd9763b3Luke Smith * will be provided the event object for the delegated event.
680f13616a493c7bf3a794982e07d10abd9763b3Luke Smith * @param el {String|node} the element that is the delegation container
680f13616a493c7bf3a794982e07d10abd9763b3Luke Smith * @param spec {string|Function} a selector that must match the target of the
680f13616a493c7bf3a794982e07d10abd9763b3Luke Smith * @param context optional argument that specifies what 'this' refers to.
680f13616a493c7bf3a794982e07d10abd9763b3Luke Smith * @param args* 0..n additional arguments to pass on to the callback function.
680f13616a493c7bf3a794982e07d10abd9763b3Luke Smith * These arguments will be added after the event object.
680f13616a493c7bf3a794982e07d10abd9763b3Luke Smith * @return {EventHandle} the detach handle
73cf5b20418beae941f34ec39a8d87035ae01711Luke Smith Y.log("delegate requires type, callback, parent, & filter", "warn");
73cf5b20418beae941f34ec39a8d87035ae01711Luke Smith container = (query) ? Y.Selector.query(query, null, true) : el;
73cf5b20418beae941f34ec39a8d87035ae01711Luke Smith args.splice(2, 2, container); // remove the filter
73cf5b20418beae941f34ec39a8d87035ae01711Luke Smith categories = detachCategories[cat] || (detachCategories[cat] = {});
73cf5b20418beae941f34ec39a8d87035ae01711Luke Smith categories = categories[type] || (categories[type] = []);
680f13616a493c7bf3a794982e07d10abd9763b3Luke Smith * Overrides the <code>_notify</code> method on the normal DOM subscription to inject the filtering logic and only proceed in the case of a match.
680f13616a493c7bf3a794982e07d10abd9763b3Luke Smith * @method delegate.notifySub
680f13616a493c7bf3a794982e07d10abd9763b3Luke Smith * @param thisObj {Object} default 'this' object for the callback
680f13616a493c7bf3a794982e07d10abd9763b3Luke Smith * @param args {Array} arguments passed to the event's <code>fire()</code>
680f13616a493c7bf3a794982e07d10abd9763b3Luke Smith * @param ce {CustomEvent} the custom event managing the DOM subscriptions for
680f13616a493c7bf3a794982e07d10abd9763b3Luke Smith * the subscribed event on the subscribing node.
680f13616a493c7bf3a794982e07d10abd9763b3Luke Smith * @return {Boolean} false if the event was stopped
680f13616a493c7bf3a794982e07d10abd9763b3Luke Smith * @since 3.2.0
7b4d1363155303b5bfb852e5639b851bbd4dc255Luke Smithdelegate.notifySub = function (thisObj, args, ce) {
7b4d1363155303b5bfb852e5639b851bbd4dc255Luke Smith // Preserve args for other subscribers
7b4d1363155303b5bfb852e5639b851bbd4dc255Luke Smith // Only notify subs if the event occurred on a targeted element
7b4d1363155303b5bfb852e5639b851bbd4dc255Luke Smith var currentTarget = this.getCurrentTarget.apply(this, args),
14017741ef334485c65013d129608513161db7c2Luke Smith // Support multiple matches up the the container subtree
14017741ef334485c65013d129608513161db7c2Luke Smith for (i = currentTarget.length - 1; i >= 0; --i) {
14017741ef334485c65013d129608513161db7c2Luke Smith // New facade to avoid corrupting facade sent to direct subs
14017741ef334485c65013d129608513161db7c2Luke Smith args[0] = new Y.DOMEventFacade(originalEvent, target, ce);
0c4bb3413c8172f27fcebdf7242f5798d026064bLuke Smith if (ret === false) { // stop further notifications
680f13616a493c7bf3a794982e07d10abd9763b3Luke Smith * <p>Compiles a selector string into a filter function that returns an array of Nodes matching that selector starging from the event's target up the parent axis to the Node from which the subscription occurred. If only one Node matches, it is returned (absent the array), and if none, undefined is returned.</p>
680f13616a493c7bf3a794982e07d10abd9763b3Luke Smith * <p>Previously compiled filter functions are returned if the same selector string is provided.</p>
680f13616a493c7bf3a794982e07d10abd9763b3Luke Smith * <p>This function may be useful when defining synthetic events for delegate handling.</p>
680f13616a493c7bf3a794982e07d10abd9763b3Luke Smith * @method delegate.compileFilter
680f13616a493c7bf3a794982e07d10abd9763b3Luke Smith * @param selector {String} the selector string to base the filtration on
680f13616a493c7bf3a794982e07d10abd9763b3Luke Smith * @return {Function}
680f13616a493c7bf3a794982e07d10abd9763b3Luke Smith * @since 3.2.0
7b4d1363155303b5bfb852e5639b851bbd4dc255Luke Smithdelegate.compileFilter = Y.cached(function (selector) {
7b4d1363155303b5bfb852e5639b851bbd4dc255Luke Smith return function (e) {
67d8986c26b16b1d9ea6e5b62e753c55af9a4f52Todd Kloots * Sets up event delegation on a container element. The delegated event
7b4d1363155303b5bfb852e5639b851bbd4dc255Luke Smith * will use a supplied filter to test if the callback should be executed.
7b4d1363155303b5bfb852e5639b851bbd4dc255Luke Smith * This filter can be either a selector string or a function that returns
7b4d1363155303b5bfb852e5639b851bbd4dc255Luke Smith * a Node to use as the currentTarget for the event.
67d8986c26b16b1d9ea6e5b62e753c55af9a4f52Todd Kloots * The event object for the delegated event is supplied to the callback
67d8986c26b16b1d9ea6e5b62e753c55af9a4f52Todd Kloots * function. It is modified slightly in order to support all properties
67d8986c26b16b1d9ea6e5b62e753c55af9a4f52Todd Kloots * that may be needed for event delegation. 'currentTarget' is set to
7b4d1363155303b5bfb852e5639b851bbd4dc255Luke Smith * the element that matched the selector string filter or the Node returned
7b4d1363155303b5bfb852e5639b851bbd4dc255Luke Smith * from the filter function. 'container' is set to the element that the
7b4d1363155303b5bfb852e5639b851bbd4dc255Luke Smith * listener is delegated from (this normally would be the 'currentTarget').
7b4d1363155303b5bfb852e5639b851bbd4dc255Luke Smith * Filter functions will be called with the arguments that would be passed to
7b4d1363155303b5bfb852e5639b851bbd4dc255Luke Smith * the callback function, including the event object as the first parameter.
7b4d1363155303b5bfb852e5639b851bbd4dc255Luke Smith * The function should return false (or a falsey value) if the success criteria
7b4d1363155303b5bfb852e5639b851bbd4dc255Luke Smith * aren't met, and the Node to use as the event's currentTarget and 'this'
7b4d1363155303b5bfb852e5639b851bbd4dc255Luke Smith * object if they are.
67d8986c26b16b1d9ea6e5b62e753c55af9a4f52Todd Kloots * @method delegate
67d8986c26b16b1d9ea6e5b62e753c55af9a4f52Todd Kloots * @param type {string} the event type to delegate
67d8986c26b16b1d9ea6e5b62e753c55af9a4f52Todd Kloots * @param fn {function} the callback function to execute. This function
67d8986c26b16b1d9ea6e5b62e753c55af9a4f52Todd Kloots * will be provided the event object for the delegated event.
67d8986c26b16b1d9ea6e5b62e753c55af9a4f52Todd Kloots * @param el {string|node} the element that is the delegation container
7b4d1363155303b5bfb852e5639b851bbd4dc255Luke Smith * @param filter {string|function} a selector that must match the target of the
7b4d1363155303b5bfb852e5639b851bbd4dc255Luke Smith * event or a function that returns a Node or false.
67d8986c26b16b1d9ea6e5b62e753c55af9a4f52Todd Kloots * @param context optional argument that specifies what 'this' refers to.
67d8986c26b16b1d9ea6e5b62e753c55af9a4f52Todd Kloots * @param args* 0..n additional arguments to pass on to the callback function.
67d8986c26b16b1d9ea6e5b62e753c55af9a4f52Todd Kloots * These arguments will be added after the event object.
67d8986c26b16b1d9ea6e5b62e753c55af9a4f52Todd Kloots * @return {EventHandle} the detach handle