delegate.js revision 10d8bafc5c24f3a4285cf6060a1935ba5cfc4b85
3f3aa287185afb5d48d7ef0717054a154c372dc9Adam Moore * Adds event delegation support to the library.
3f3aa287185afb5d48d7ef0717054a154c372dc9Adam Moore * @module event
3f3aa287185afb5d48d7ef0717054a154c372dc9Adam Moore * @submodule event-delegate
b839e41217a63e244d65c3aadf54feec82ddd179Luke Smithvar toArray = Y.Array,
b839e41217a63e244d65c3aadf54feec82ddd179Luke Smith * <p>Sets up event delegation on a container element. The delegated event
b839e41217a63e244d65c3aadf54feec82ddd179Luke Smith * will use a supplied selector or filtering function to test if the event
b839e41217a63e244d65c3aadf54feec82ddd179Luke Smith * references at least one node that should trigger the subscription
b839e41217a63e244d65c3aadf54feec82ddd179Luke Smith * callback.</p>
b839e41217a63e244d65c3aadf54feec82ddd179Luke Smith * <p>Selector string filters will trigger the callback if the event originated
b839e41217a63e244d65c3aadf54feec82ddd179Luke Smith * from a node that matches it or is contained in a node that matches it.
b839e41217a63e244d65c3aadf54feec82ddd179Luke Smith * Function filters are called for each Node up the parent axis to the
b839e41217a63e244d65c3aadf54feec82ddd179Luke Smith * subscribing container node, and receive at each level the Node and the event
b839e41217a63e244d65c3aadf54feec82ddd179Luke Smith * object. The function should return true (or a truthy value) if that Node
b839e41217a63e244d65c3aadf54feec82ddd179Luke Smith * should trigger the subscription callback. Note, it is possible for filters
b839e41217a63e244d65c3aadf54feec82ddd179Luke Smith * to match multiple Nodes for a single event. In this case, the delegate
b839e41217a63e244d65c3aadf54feec82ddd179Luke Smith * callback will be executed for each matching Node.</p>
b839e41217a63e244d65c3aadf54feec82ddd179Luke Smith * <p>For each matching Node, the callback will be executed with its 'this'
b839e41217a63e244d65c3aadf54feec82ddd179Luke Smith * object set to the Node matched by the filter (unless a specific context was
b839e41217a63e244d65c3aadf54feec82ddd179Luke Smith * provided during subscription), and the provided event's
b839e41217a63e244d65c3aadf54feec82ddd179Luke Smith * <code>currentTarget</code> will also be set to the matching Node. The
b839e41217a63e244d65c3aadf54feec82ddd179Luke Smith * containing Node from which the subscription was originally made can be
b839e41217a63e244d65c3aadf54feec82ddd179Luke Smith * referenced as <code>e.container</code>.
b839e41217a63e244d65c3aadf54feec82ddd179Luke Smith * @method delegate
b839e41217a63e244d65c3aadf54feec82ddd179Luke Smith * @param type {String} the event type to delegate
b839e41217a63e244d65c3aadf54feec82ddd179Luke Smith * @param fn {Function} the callback function to execute. This function
b839e41217a63e244d65c3aadf54feec82ddd179Luke Smith * will be provided the event object for the delegated event.
b839e41217a63e244d65c3aadf54feec82ddd179Luke Smith * @param el {String|node} the element that is the delegation container
b839e41217a63e244d65c3aadf54feec82ddd179Luke Smith * @param spec {string|Function} a selector that must match the target of the
b839e41217a63e244d65c3aadf54feec82ddd179Luke Smith * event or a function to test target and its parents for a match
b839e41217a63e244d65c3aadf54feec82ddd179Luke Smith * @param context optional argument that specifies what 'this' refers to.
b839e41217a63e244d65c3aadf54feec82ddd179Luke Smith * @param args* 0..n additional arguments to pass on to the callback function.
b839e41217a63e244d65c3aadf54feec82ddd179Luke Smith * These arguments will be added after the event object.
b839e41217a63e244d65c3aadf54feec82ddd179Luke Smith * @return {EventHandle} the detach handle
b839e41217a63e244d65c3aadf54feec82ddd179Luke Smith typeBits, synth, container, categories, cat, i, len, handles, handle;
b839e41217a63e244d65c3aadf54feec82ddd179Luke Smith // Support Y.delegate({ click: fnA, key: fnB }, context, filter, ...);
b839e41217a63e244d65c3aadf54feec82ddd179Luke Smith // and Y.delegate(['click', 'key'], fn, context, filter, ...);
b839e41217a63e244d65c3aadf54feec82ddd179Luke Smith // Y.delegate({'click', fn}, context, filter) =>
b839e41217a63e244d65c3aadf54feec82ddd179Luke Smith // Y.delegate('click', fn, context, filter)
b839e41217a63e244d65c3aadf54feec82ddd179Luke Smith args.unshift(null); // one arg becomes two; need to make space
b839e41217a63e244d65c3aadf54feec82ddd179Luke Smith for (i in type) {
b839e41217a63e244d65c3aadf54feec82ddd179Luke Smith Y.log("delegate requires type, callback, parent, & filter", "warn");
b839e41217a63e244d65c3aadf54feec82ddd179Luke Smith container = (query) ? Y.Selector.query(query, null, true) : el;
b839e41217a63e244d65c3aadf54feec82ddd179Luke Smith args.splice(2, 2, container); // remove the filter
b839e41217a63e244d65c3aadf54feec82ddd179Luke Smith handle = Y.Event._attach(args, { facade: false });
b839e41217a63e244d65c3aadf54feec82ddd179Luke Smith categories = detachCategories[cat] || (detachCategories[cat] = {});
b839e41217a63e244d65c3aadf54feec82ddd179Luke Smith categories = categories[type] || (categories[type] = []);
5af2618af73bd3f008ef0e9b5f982f560c64059bAdam MooreOverrides the <code>_notify</code> method on the normal DOM subscription to
b839e41217a63e244d65c3aadf54feec82ddd179Luke Smithinject the filtering logic and only proceed in the case of a match.
b839e41217a63e244d65c3aadf54feec82ddd179Luke SmithThis method is hosted as a private property of the `delegate` method
b839e41217a63e244d65c3aadf54feec82ddd179Luke Smith(e.g. `Y.delegate.notifySub`)
b839e41217a63e244d65c3aadf54feec82ddd179Luke Smith@method notifySub
b839e41217a63e244d65c3aadf54feec82ddd179Luke Smith@param thisObj {Object} default 'this' object for the callback
b839e41217a63e244d65c3aadf54feec82ddd179Luke Smith@param args {Array} arguments passed to the event's <code>fire()</code>
b839e41217a63e244d65c3aadf54feec82ddd179Luke Smith@param ce {CustomEvent} the custom event managing the DOM subscriptions for
b839e41217a63e244d65c3aadf54feec82ddd179Luke Smith the subscribed event on the subscribing node.
b839e41217a63e244d65c3aadf54feec82ddd179Luke Smith@return {Boolean} false if the event was stopped
b839e41217a63e244d65c3aadf54feec82ddd179Luke Smithdelegate.notifySub = function (thisObj, args, ce) {
b839e41217a63e244d65c3aadf54feec82ddd179Luke Smith // Preserve args for other subscribers
5af2618af73bd3f008ef0e9b5f982f560c64059bAdam Moore // Only notify subs if the event occurred on a targeted element
5af2618af73bd3f008ef0e9b5f982f560c64059bAdam Moore var currentTarget = delegate._applyFilter(this.filter, args, ce),
5af2618af73bd3f008ef0e9b5f982f560c64059bAdam Moore //container = e.currentTarget,
5af2618af73bd3f008ef0e9b5f982f560c64059bAdam Moore // Support multiple matches up the the container subtree
5af2618af73bd3f008ef0e9b5f982f560c64059bAdam Moore // The second arg is the currentTarget, but we'll be reusing this
b839e41217a63e244d65c3aadf54feec82ddd179Luke Smith // facade, replacing the currentTarget for each use, so it doesn't
return ret;
return function (target, e) {
match = [],
isContainer = false;
while (target) {
if (isContainer) {
while (target) {
return match;