dom-delegate
Advanced tools
Comparing version 0.1.3 to 0.1.5
@@ -422,2 +422,43 @@ /*global buster, assert, refute, Delegate*/ | ||
}, | ||
// Regression test for - https://github.com/ftlabs/dom-delegate/pull/10 | ||
'Regression test: event listener should be rebound after last event is removed and new events are added.' : function() { | ||
var delegate, spy, element, textNode; | ||
spy = this.spy(); | ||
delegate = new Delegate(document); | ||
delegate.on('click', '#delegate-test-clickable', spy); | ||
// Unbind event listeners | ||
delegate.off(); | ||
delegate.on('click', '#delegate-test-clickable', spy); | ||
element = document.getElementById('delegate-test-clickable'); | ||
textNode = document.createTextNode('Test text'); | ||
element.appendChild(textNode); | ||
textNode.dispatchEvent(setupHelper.getMouseEvent('click')); | ||
assert.called(spy); | ||
delegate.off(); | ||
}, | ||
// Test for issue #5 | ||
'The root element, via a null selector, is supported': function() { | ||
var delegate, spy, element; | ||
delegate = new Delegate(document.body); | ||
spy = this.spy(); | ||
delegate.on('click', null, spy); | ||
element = document.body; | ||
element.dispatchEvent(setupHelper.getMouseEvent('click')); | ||
assert.calledOnce(spy); | ||
delegate.off(); | ||
}, | ||
'tearDown': function() { | ||
@@ -424,0 +465,0 @@ setupHelper.tearDown(); |
{ | ||
"name": "dom-delegate", | ||
"description": "Create and manage a DOM event delegator.", | ||
"version": "0.1.3", | ||
"version": "0.1.5", | ||
"main": "lib/delegate.js", | ||
@@ -6,0 +6,0 @@ "scripts": [ |
@@ -0,5 +1,8 @@ | ||
/*jslint browser:true, node:true*/ | ||
/*global define, Node*/ | ||
/** | ||
* @preserve Create and manage a DOM event delegator. | ||
* | ||
* @version 0.1.3 | ||
* @version 0.1.5 | ||
* @codingstandard ftlabs-jsv2 | ||
@@ -10,400 +13,437 @@ * @copyright The Financial Times Limited [All Rights Reserved] | ||
/*jslint browser:true, node:true*/ | ||
/*global define, Node*/ | ||
;(function(){ | ||
/** | ||
* DOM event delegator | ||
* | ||
* The delegator will listen for events that bubble up to the root node. | ||
* | ||
* @constructor | ||
* @param {Node|string} root The root node or a selector string matching the root node | ||
*/ | ||
function Delegate(root) { | ||
'use strict'; | ||
var self = this; | ||
if (root) { | ||
this.root(root); | ||
/** | ||
* DOM event delegator | ||
* | ||
* The delegator will listen | ||
* for events that bubble up | ||
* to the root node. | ||
* | ||
* @constructor | ||
* @param {Node|string} root The root node or a selector string matching the root node | ||
*/ | ||
function Delegate(root) { | ||
var self = this; | ||
if (root) { | ||
this.root(root); | ||
} | ||
/** | ||
* Maintain a map of listener | ||
* lists, keyed by event name. | ||
* | ||
* @type Object | ||
*/ | ||
this.listenerMap = {}; | ||
/** @type function() */ | ||
this.handle = function(event) { Delegate.prototype.handle.call(self, event); }; | ||
} | ||
/** | ||
* @protected | ||
* @type ?boolean | ||
*/ | ||
Delegate.tagsCaseSensitive = null; | ||
/** | ||
* Maintain a map of listener lists, keyed by event name. | ||
* Start listening for events | ||
* on the provided DOM element | ||
* | ||
* @type Object | ||
* @param {Node} root The root node or a selector string matching the root node | ||
*/ | ||
this.listenerMap = {}; | ||
Delegate.prototype.root = function(root) { | ||
var listenerMap = this.listenerMap; | ||
var eventType; | ||
if (typeof root === 'string') { | ||
root = document.querySelector(root); | ||
} | ||
/** @type function() */ | ||
this.handle = function(event) { Delegate.prototype.handle.call(self, event); }; | ||
} | ||
// Remove master event listeners | ||
if (this.rootElement) { | ||
for (eventType in listenerMap) { | ||
if (listenerMap.hasOwnProperty(eventType)) { | ||
this.rootElement.removeEventListener(eventType, this.handle, this.captureForType(eventType)); | ||
} | ||
} | ||
} | ||
// If no root or root is not | ||
// a dom node, then remove internal | ||
// root reference and exit here | ||
if (!root || !root.addEventListener) { | ||
if (this.rootElement) { | ||
delete this.rootElement; | ||
} | ||
return; | ||
} | ||
/** | ||
* @protected | ||
* @type ?boolean | ||
*/ | ||
Delegate.tagsCaseSensitive = null; | ||
/** | ||
* The root node at which | ||
* listeners are attached. | ||
* | ||
* @type Node | ||
*/ | ||
this.rootElement = root; | ||
/** | ||
* Start listening for events on the provided DOM element | ||
* @param {Node} root The root node or a selector string matching the root node | ||
*/ | ||
Delegate.prototype.root = function(root) { | ||
'use strict'; | ||
var listenerMap = this.listenerMap; | ||
var eventType; | ||
if (typeof root === 'string') { | ||
root = document.querySelector(root); | ||
} | ||
// Remove master event listeners | ||
if (this.rootElement) { | ||
// Set up master event listeners | ||
for (eventType in listenerMap) { | ||
if (listenerMap.hasOwnProperty(eventType)) { | ||
this.rootElement.removeEventListener(eventType, this.handle, this.captureForType(eventType)); | ||
this.rootElement.addEventListener(eventType, this.handle, this.captureForType(eventType)); | ||
} | ||
} | ||
} | ||
}; | ||
// If no root or root is not a dom node, then | ||
// remove internal root reference and exit here | ||
if (!root || !root.addEventListener) { | ||
if (this.rootElement) { | ||
delete this.rootElement; | ||
} | ||
return; | ||
} | ||
/** | ||
* @param {string} eventType | ||
* @returns boolean | ||
*/ | ||
Delegate.prototype.captureForType = function(eventType) { | ||
return eventType === 'error'; | ||
}; | ||
/** | ||
* The root node at which listeners are attached. | ||
* Attach a handler to one | ||
* event for all elements | ||
* that match the selector, | ||
* now or in the future | ||
* | ||
* @type Node | ||
* The handler function receives | ||
* three arguments: the DOM event | ||
* object, the node that matched | ||
* the selector while the event | ||
* was bubbling and a reference | ||
* to itself. Within the handler, | ||
* 'this' is equal to the second | ||
* argument. | ||
* | ||
* The node that actually received | ||
* the event can be accessed via | ||
* 'event.target'. | ||
* | ||
* @param {string} eventType Listen for these events (in a space-separated list) | ||
* @param {string} selector Only handle events on elements matching this selector | ||
* @param {function()} handler Handler function - event data passed here will be in event.data | ||
* @param {Object} [eventData] Data to pass in event.data | ||
* @returns {Delegate} This method is chainable | ||
*/ | ||
this.rootElement = root; | ||
Delegate.prototype.on = function(eventType, selector, handler, eventData) { | ||
var root, listenerMap, matcher, matcherParam, self = this, /** @const */ SEPARATOR = ' '; | ||
// Set up master event listeners | ||
for (eventType in listenerMap) { | ||
if (listenerMap.hasOwnProperty(eventType)) { | ||
this.rootElement.addEventListener(eventType, this.handle, this.captureForType(eventType)); | ||
if (!eventType) { | ||
throw new TypeError('Invalid event type: ' + eventType); | ||
} | ||
} | ||
}; | ||
// Support a separated list of event types | ||
if (eventType.indexOf(SEPARATOR) !== -1) { | ||
eventType.split(SEPARATOR).forEach(function(singleEventType) { | ||
self.on(singleEventType, selector, handler, eventData); | ||
}); | ||
/** | ||
* @param {string} eventType | ||
* @returns boolean | ||
*/ | ||
Delegate.prototype.captureForType = function(eventType) { | ||
'use strict'; | ||
return eventType === 'error'; | ||
}; | ||
/** | ||
* Attach a handler to one event for all elements that match the selector, now or in the future | ||
* | ||
* The handler function receives three arguments: the DOM event object, the node that matched the selector while the event was bubbling | ||
* and a reference to itself. Within the handler, 'this' is equal to the second argument. | ||
* The node that actually received the event can be accessed via 'event.target'. | ||
* | ||
* @param {string} eventType Listen for these events (in a space-separated list) | ||
* @param {string} selector Only handle events on elements matching this selector | ||
* @param {function()} handler Handler function - event data passed here will be in event.data | ||
* @param {Object} [eventData] Data to pass in event.data | ||
* @returns {Delegate} This method is chainable | ||
*/ | ||
Delegate.prototype.on = function(eventType, selector, handler, eventData) { | ||
'use strict'; | ||
var root, listenerMap, matcher, matcherParam, self = this, /** @const */ SEPARATOR = ' '; | ||
if (!eventType) { | ||
throw new TypeError('Invalid event type: ' + eventType); | ||
} | ||
if (!selector) { | ||
throw new TypeError('Invalid selector: ' + selector); | ||
} | ||
// Support a separated list of event types | ||
if (eventType.indexOf(SEPARATOR) !== -1) { | ||
eventType.split(SEPARATOR).forEach(function(singleEventType) { | ||
self.on(singleEventType, selector, handler, eventData); | ||
}); | ||
return this; | ||
} | ||
// Normalise undefined eventData to null | ||
if (eventData === undefined) { | ||
eventData = null; | ||
} | ||
if (typeof handler !== 'function') { | ||
throw new TypeError('Handler must be a type of Function'); | ||
} | ||
root = this.rootElement; | ||
listenerMap = this.listenerMap; | ||
// Add master handler for type if not created yet | ||
if (!listenerMap[eventType]) { | ||
if (root) { | ||
root.addEventListener(eventType, this.handle, this.captureForType(eventType)); | ||
return this; | ||
} | ||
listenerMap[eventType] = []; | ||
} | ||
// Compile a matcher for the given selector | ||
if (/^[a-z]+$/i.test(selector)) { | ||
// Lazily check whether tag names are case sensitive (as in XML or XHTML documents). | ||
if (Delegate.tagsCaseSensitive === null) { | ||
Delegate.tagsCaseSensitive = document.createElement('i').tagName === 'i'; | ||
// Normalise undefined eventData to null | ||
if (eventData === undefined) { | ||
eventData = null; | ||
} | ||
if (!Delegate.tagsCaseSensitive) { | ||
matcherParam = selector.toUpperCase(); | ||
} else { | ||
matcherParam = selector; | ||
if (typeof handler !== 'function') { | ||
throw new TypeError('Handler must be a type of Function'); | ||
} | ||
matcher = this.matchesTag; | ||
} else if (/^#[a-z0-9\-_]+$/i.test(selector)) { | ||
matcherParam = selector.slice(1); | ||
matcher = this.matchesId; | ||
} else { | ||
matcherParam = selector; | ||
matcher = this.matches; | ||
} | ||
root = this.rootElement; | ||
listenerMap = this.listenerMap; | ||
// Add to the list of listeners | ||
listenerMap[eventType].push({ | ||
selector: selector, | ||
eventData: eventData, | ||
handler: handler, | ||
matcher: matcher, | ||
matcherParam: matcherParam | ||
}); | ||
// Add master handler for type if not created yet | ||
if (!listenerMap[eventType]) { | ||
if (root) { | ||
root.addEventListener(eventType, this.handle, this.captureForType(eventType)); | ||
} | ||
listenerMap[eventType] = []; | ||
} | ||
return this; | ||
}; | ||
if (!selector) { | ||
matcherParam = null; | ||
// COMPLEX - matchesRoot needs to have access to | ||
// this.rootElement, so bind the function to this. | ||
matcher = this.matchesRoot.bind(this); | ||
/** | ||
* Remove an event handler for elements that match the selector, forever | ||
* | ||
* @param {string} eventType Remove handlers for events matching this type, considering the other parameters | ||
* @param {string} [selector] If this parameter is omitted, only handlers which match the other two will be removed | ||
* @param {function()} [handler] If this parameter is omitted, only handlers which match the previous two will be removed | ||
* @returns {Delegate} This method is chainable | ||
*/ | ||
Delegate.prototype.off = function(eventType, selector, handler) { | ||
'use strict'; | ||
var i, listener, listenerMap, listenerList, singleEventType, self = this, /** @const */ SEPARATOR = ' '; | ||
// Compile a matcher for the given selector | ||
} else if (/^[a-z]+$/i.test(selector)) { | ||
listenerMap = this.listenerMap; | ||
if (!eventType) { | ||
for (singleEventType in listenerMap) { | ||
if (listenerMap.hasOwnProperty(singleEventType)) { | ||
this.off(singleEventType, selector, handler); | ||
// Lazily check whether tag names are case sensitive (as in XML or XHTML documents). | ||
if (Delegate.tagsCaseSensitive === null) { | ||
Delegate.tagsCaseSensitive = document.createElement('i').tagName === 'i'; | ||
} | ||
} | ||
return this; | ||
} | ||
if (!Delegate.tagsCaseSensitive) { | ||
matcherParam = selector.toUpperCase(); | ||
} else { | ||
matcherParam = selector; | ||
} | ||
listenerList = listenerMap[eventType]; | ||
if (!listenerList || !listenerList.length) { | ||
return this; | ||
} | ||
matcher = this.matchesTag; | ||
} else if (/^#[a-z0-9\-_]+$/i.test(selector)) { | ||
matcherParam = selector.slice(1); | ||
matcher = this.matchesId; | ||
} else { | ||
matcherParam = selector; | ||
matcher = this.matches; | ||
} | ||
// Support a separated list of event types | ||
if (eventType.indexOf(SEPARATOR) !== -1) { | ||
eventType.split(SEPARATOR).forEach(function(singleEventType) { | ||
self.off(singleEventType, selector, handler); | ||
// Add to the list of listeners | ||
listenerMap[eventType].push({ | ||
selector: selector, | ||
eventData: eventData, | ||
handler: handler, | ||
matcher: matcher, | ||
matcherParam: matcherParam | ||
}); | ||
return this; | ||
} | ||
}; | ||
// Remove only parameter matches if specified | ||
for (i = listenerList.length - 1; i >= 0; i--) { | ||
listener = listenerList[i]; | ||
/** | ||
* Remove an event handler | ||
* for elements that match | ||
* the selector, forever | ||
* | ||
* @param {string} eventType Remove handlers for events matching this type, considering the other parameters | ||
* @param {string} [selector] If this parameter is omitted, only handlers which match the other two will be removed | ||
* @param {function()} [handler] If this parameter is omitted, only handlers which match the previous two will be removed | ||
* @returns {Delegate} This method is chainable | ||
*/ | ||
Delegate.prototype.off = function(eventType, selector, handler) { | ||
var i, listener, listenerMap, listenerList, singleEventType, self = this, /** @const */ SEPARATOR = ' '; | ||
if ((!selector || selector === listener.selector) && (!handler || handler === listener.handler)) { | ||
listenerList.splice(i, 1); | ||
listenerMap = this.listenerMap; | ||
if (!eventType) { | ||
for (singleEventType in listenerMap) { | ||
if (listenerMap.hasOwnProperty(singleEventType)) { | ||
this.off(singleEventType, selector, handler); | ||
} | ||
} | ||
return this; | ||
} | ||
} | ||
// All listeners removed | ||
if (!listenerList.length) { | ||
delete listenerList[eventType]; | ||
// Remove the main handler | ||
if (this.rootElement) { | ||
this.rootElement.removeEventListener(eventType, this.handle, this.captureForType(eventType)); | ||
listenerList = listenerMap[eventType]; | ||
if (!listenerList || !listenerList.length) { | ||
return this; | ||
} | ||
} | ||
return this; | ||
}; | ||
// Support a separated list of event types | ||
if (eventType.indexOf(SEPARATOR) !== -1) { | ||
eventType.split(SEPARATOR).forEach(function(singleEventType) { | ||
self.off(singleEventType, selector, handler); | ||
}); | ||
return this; | ||
} | ||
/** | ||
* Handle an arbitrary event. | ||
* | ||
* @param {Event} event | ||
*/ | ||
Delegate.prototype.handle = function(event) { | ||
'use strict'; | ||
var i, l, root, listener, returned, listenerList, target, /** @const */ EVENTIGNORE = 'ftLabsDelegateIgnore'; | ||
// Remove only parameter matches if specified | ||
for (i = listenerList.length - 1; i >= 0; i--) { | ||
listener = listenerList[i]; | ||
if (event[EVENTIGNORE] === true) { | ||
return; | ||
} | ||
if ((!selector || selector === listener.selector) && (!handler || handler === listener.handler)) { | ||
listenerList.splice(i, 1); | ||
} | ||
} | ||
target = event.target; | ||
if (target.nodeType === Node.TEXT_NODE) { | ||
target = target.parentNode; | ||
} | ||
// All listeners removed | ||
if (!listenerList.length) { | ||
delete listenerMap[eventType]; | ||
root = this.rootElement; | ||
listenerList = this.listenerMap[event.type]; | ||
// Remove the main handler | ||
if (this.rootElement) { | ||
this.rootElement.removeEventListener(eventType, this.handle, this.captureForType(eventType)); | ||
} | ||
} | ||
// Need to continuously check that the specific list is still populated in case one of the callbacks actually causes the list to be destroyed. | ||
l = listenerList.length; | ||
while (target && l) { | ||
for (i = 0; i < l; i++) { | ||
listener = listenerList[i]; | ||
return this; | ||
}; | ||
// Bail from this loop if the length changed and no more listeners are defined between i and l. | ||
if (!listener) { | ||
break; | ||
} | ||
// Check for match and fire the event if there's one | ||
// TODO:MCG:20120117: Need a way to check if event#stopImmediateProgagation was called. If so, break both loops. | ||
if (listener.matcher.call(target, listener.matcherParam, target)) { | ||
returned = this.fire(event, target, listener); | ||
} | ||
/** | ||
* Handle an arbitrary event. | ||
* | ||
* @param {Event} event | ||
*/ | ||
Delegate.prototype.handle = function(event) { | ||
var i, l, root, listener, returned, listenerList, target, /** @const */ EVENTIGNORE = 'ftLabsDelegateIgnore'; | ||
// Stop propagation to subsequent callbacks if the callback returned false | ||
if (returned === false) { | ||
event[EVENTIGNORE] = true; | ||
return; | ||
} | ||
if (event[EVENTIGNORE] === true) { | ||
return; | ||
} | ||
// TODO:MCG:20120117: Need a way to check if event#stopProgagation was called. If so, break looping through the DOM. | ||
// Stop if the delegation root has been reached | ||
if (target === root) { | ||
break; | ||
target = event.target; | ||
if (target.nodeType === Node.TEXT_NODE) { | ||
target = target.parentNode; | ||
} | ||
root = this.rootElement; | ||
listenerList = this.listenerMap[event.type]; | ||
// Need to continuously check | ||
// that the specific list is | ||
// still populated in case one | ||
// of the callbacks actually | ||
// causes the list to be destroyed. | ||
l = listenerList.length; | ||
target = target.parentElement; | ||
} | ||
}; | ||
while (target && l) { | ||
for (i = 0; i < l; i++) { | ||
listener = listenerList[i]; | ||
// Bail from this loop if | ||
// the length changed and | ||
// no more listeners are | ||
// defined between i and l. | ||
if (!listener) { | ||
break; | ||
} | ||
/** | ||
* Fire a listener on a target. | ||
* | ||
* @param {Event} event | ||
* @param {Node} target | ||
* @param {Object} listener | ||
* @returns {boolean} | ||
*/ | ||
Delegate.prototype.fire = function(event, target, listener) { | ||
'use strict'; | ||
var returned, oldData; | ||
// Check for match and fire | ||
// the event if there's one | ||
// | ||
// TODO:MCG:20120117: Need a way | ||
// to check if event#stopImmediateProgagation | ||
// was called. If so, break both loops. | ||
if (listener.matcher.call(target, listener.matcherParam, target)) { | ||
returned = this.fire(event, target, listener); | ||
} | ||
if (listener.eventData !== null) { | ||
oldData = event.data; | ||
event.data = listener.eventData; | ||
returned = listener.handler.call(target, event, target); | ||
event.data = oldData; | ||
} else { | ||
returned = listener.handler.call(target, event, target); | ||
} | ||
// Stop propagation to subsequent | ||
// callbacks if the callback returned | ||
// false | ||
if (returned === false) { | ||
event[EVENTIGNORE] = true; | ||
return; | ||
} | ||
} | ||
return returned; | ||
}; | ||
// TODO:MCG:20120117: Need a way to | ||
// check if event#stopProgagation | ||
// was called. If so, break looping | ||
// through the DOM. Stop if the | ||
// delegation root has been reached | ||
if (target === root) { | ||
break; | ||
} | ||
l = listenerList.length; | ||
target = target.parentElement; | ||
} | ||
}; | ||
/** | ||
* Check whether an element matches a generic selector. | ||
* | ||
* @type function() | ||
* @param {string} selector A CSS selector | ||
*/ | ||
Delegate.prototype.matches = (function(p) { | ||
'use strict'; | ||
return (p.matchesSelector || p.webkitMatchesSelector || p.mozMatchesSelector || p.msMatchesSelector || p.oMatchesSelector); | ||
}(HTMLElement.prototype)); | ||
/** | ||
* Fire a listener on a target. | ||
* | ||
* @param {Event} event | ||
* @param {Node} target | ||
* @param {Object} listener | ||
* @returns {boolean} | ||
*/ | ||
Delegate.prototype.fire = function(event, target, listener) { | ||
var returned, oldData; | ||
if (listener.eventData !== null) { | ||
oldData = event.data; | ||
event.data = listener.eventData; | ||
returned = listener.handler.call(target, event, target); | ||
event.data = oldData; | ||
} else { | ||
returned = listener.handler.call(target, event, target); | ||
} | ||
/** | ||
* Check whether an element matches a tag selector. | ||
* | ||
* Tags are NOT case-sensitive, except in XML (and XML-based languages such as XHTML). | ||
* | ||
* @param {string} tagName The tag name to test against | ||
* @param {Element} element The element to test with | ||
* @returns boolean | ||
*/ | ||
Delegate.prototype.matchesTag = function(tagName, element) { | ||
'use strict'; | ||
return tagName === element.tagName; | ||
}; | ||
return returned; | ||
}; | ||
/** | ||
* Check whether an element matches a generic selector. | ||
* | ||
* @type function() | ||
* @param {string} selector A CSS selector | ||
*/ | ||
Delegate.prototype.matches = (function(p) { | ||
return (p.matchesSelector || p.webkitMatchesSelector || p.mozMatchesSelector || p.msMatchesSelector || p.oMatchesSelector); | ||
}(HTMLElement.prototype)); | ||
/** | ||
* Check whether the ID of the element in 'this' matches the given ID. | ||
* | ||
* IDs are case-sensitive. | ||
* | ||
* @param {string} id The ID to test against | ||
* @param {Element} element The element to test with | ||
* @returns boolean | ||
*/ | ||
Delegate.prototype.matchesId = function(id, element) { | ||
'use strict'; | ||
return id === element.id; | ||
}; | ||
/** | ||
* Check whether an element | ||
* matches a tag selector. | ||
* | ||
* Tags are NOT case-sensitive, | ||
* except in XML (and XML-based | ||
* languages such as XHTML). | ||
* | ||
* @param {string} tagName The tag name to test against | ||
* @param {Element} element The element to test with | ||
* @returns boolean | ||
*/ | ||
Delegate.prototype.matchesTag = function(tagName, element) { | ||
return tagName === element.tagName; | ||
}; | ||
if (typeof define !== 'undefined' && define.amd) { | ||
/** | ||
* Check whether an element matches the root. | ||
* | ||
* @param {?String} selector In this case this is always passed through as null and not used | ||
* @param {Element} element The element to test with | ||
* @returns boolean | ||
*/ | ||
Delegate.prototype.matchesRoot = function(selector, element) { | ||
return this.rootElement === element; | ||
}; | ||
// AMD. Register as an anonymous module. | ||
define(function() { | ||
'use strict'; | ||
return Delegate; | ||
}); | ||
} | ||
/** | ||
* Check whether the ID of | ||
* the element in 'this' | ||
* matches the given ID. | ||
* | ||
* IDs are case-sensitive. | ||
* | ||
* @param {string} id The ID to test against | ||
* @param {Element} element The element to test with | ||
* @returns boolean | ||
*/ | ||
Delegate.prototype.matchesId = function(id, element) { | ||
return id === element.id; | ||
}; | ||
if (typeof module !== 'undefined' && module.exports) { | ||
module.exports = function(root) { | ||
'use strict'; | ||
return new Delegate(root); | ||
/** | ||
* Short hand for off() | ||
* and root(), ie both | ||
* with no parameters | ||
* | ||
* @return void | ||
*/ | ||
Delegate.prototype.destroy = function() { | ||
this.off(); | ||
this.root(); | ||
}; | ||
module.exports.Delegate = Delegate; | ||
} | ||
/** | ||
* Expose `Delegate` | ||
*/ | ||
if (typeof module === "object") { | ||
module.exports = function(root) { | ||
return new Delegate(root); | ||
}; | ||
module.exports.Delegate = Delegate; | ||
} else if (typeof define === "function" && define.amd) { | ||
define(function() { | ||
return Delegate; | ||
}); | ||
} else { | ||
window.Delegate = Delegate; | ||
} | ||
/** | ||
* Short hand for off() and root(), ie both with no parameters | ||
* | ||
* @return void | ||
*/ | ||
Delegate.prototype.destroy = function() { | ||
this.off(); | ||
this.root(); | ||
}; | ||
})(); |
{ | ||
"name": "dom-delegate", | ||
"version": "0.1.3", | ||
"version": "0.1.5", | ||
"author": "FT Labs <enquiries@labs.ft.com> (http://labs.ft.com/)", | ||
@@ -15,4 +15,4 @@ "description": "Create and manage a DOM event delegator.", | ||
}, | ||
"scripts" : { | ||
"test" : "node_modules/.bin/buster-test -c _tests/buster.js" | ||
"scripts": { | ||
"test": "node_modules/.bin/buster-test -c _tests/buster.js" | ||
}, | ||
@@ -19,0 +19,0 @@ "keywords": [ |
@@ -98,2 +98,4 @@ # Delegate # | ||
Null is also accepted and will match the root element set by `root()`. | ||
#### `handler (function)` #### | ||
@@ -100,0 +102,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
32523
10
760
139