Commit ec69de4b authored by Alessandro Chitolina's avatar Alessandro Chitolina Committed by XhmikosR
Browse files

allow register the same handler for different delegated selectors in eventHandler

parent 99c3119c
Showing with 106 additions and 36 deletions
+106 -36
...@@ -170,13 +170,14 @@ const EventHandler = (() => { ...@@ -170,13 +170,14 @@ const EventHandler = (() => {
} }
} }
function findHandler(events, handler) { function findHandler(events, handler, delegationSelector = null) {
for (const uid in events) { for (const uid in events) {
if (!Object.prototype.hasOwnProperty.call(events, uid)) { if (!Object.prototype.hasOwnProperty.call(events, uid)) {
continue continue
} }
if (events[uid].originalHandler === handler) { const event = events[uid]
if (event.originalHandler === handler && event.delegationSelector === delegationSelector) {
return events[uid] return events[uid]
} }
} }
...@@ -184,16 +185,7 @@ const EventHandler = (() => { ...@@ -184,16 +185,7 @@ const EventHandler = (() => {
return null return null
} }
function addHandler(element, originalTypeEvent, handler, delegationFn, oneOff) { function normalizeParams(originalTypeEvent, handler, delegationFn) {
if (typeof originalTypeEvent !== 'string' || (typeof element === 'undefined' || element === null)) {
return
}
if (!handler) {
handler = delegationFn
delegationFn = null
}
const delegation = typeof handler === 'string' const delegation = typeof handler === 'string'
const originalHandler = delegation ? delegationFn : handler const originalHandler = delegation ? delegationFn : handler
...@@ -210,9 +202,24 @@ const EventHandler = (() => { ...@@ -210,9 +202,24 @@ const EventHandler = (() => {
typeEvent = originalTypeEvent typeEvent = originalTypeEvent
} }
return [delegation, originalHandler, typeEvent]
}
function addHandler(element, originalTypeEvent, handler, delegationFn, oneOff) {
if (typeof originalTypeEvent !== 'string' || (typeof element === 'undefined' || element === null)) {
return
}
if (!handler) {
handler = delegationFn
delegationFn = null
}
const [delegation, originalHandler, typeEvent] = normalizeParams(originalTypeEvent, handler, delegationFn)
const events = getEvent(element) const events = getEvent(element)
const handlers = events[typeEvent] || (events[typeEvent] = {}) const handlers = events[typeEvent] || (events[typeEvent] = {})
const previousFn = findHandler(handlers, originalHandler) const previousFn = findHandler(handlers, originalHandler, delegation ? handler : null)
if (previousFn) { if (previousFn) {
previousFn.oneOff = previousFn.oneOff && oneOff previousFn.oneOff = previousFn.oneOff && oneOff
...@@ -222,22 +229,23 @@ const EventHandler = (() => { ...@@ -222,22 +229,23 @@ const EventHandler = (() => {
const uid = getUidEvent(originalHandler, originalTypeEvent.replace(namespaceRegex, '')) const uid = getUidEvent(originalHandler, originalTypeEvent.replace(namespaceRegex, ''))
const fn = !delegation ? bootstrapHandler(element, handler) : bootstrapDelegationHandler(element, handler, delegationFn) const fn = !delegation ? bootstrapHandler(element, handler) : bootstrapDelegationHandler(element, handler, delegationFn)
fn.isDelegation = delegation fn.delegationSelector = delegation ? handler : null
fn.originalHandler = originalHandler fn.originalHandler = originalHandler
fn.oneOff = oneOff fn.oneOff = oneOff
fn.uidEvent = uid
handlers[uid] = fn handlers[uid] = fn
element.addEventListener(typeEvent, fn, delegation) element.addEventListener(typeEvent, fn, delegation)
} }
function removeHandler(element, events, typeEvent, handler) { function removeHandler(element, events, typeEvent, handler, delegationSelector) {
const fn = findHandler(events[typeEvent], handler) const fn = findHandler(events[typeEvent], handler, delegationSelector)
if (fn === null) { if (fn === null) {
return return
} }
element.removeEventListener(typeEvent, fn, fn.isDelegation) element.removeEventListener(typeEvent, fn, Boolean(delegationSelector))
delete events[typeEvent][uidEvent] delete events[typeEvent][fn.uidEvent]
} }
function removeNamespacedHandlers(element, events, typeEvent, namespace) { function removeNamespacedHandlers(element, events, typeEvent, namespace) {
...@@ -248,7 +256,8 @@ const EventHandler = (() => { ...@@ -248,7 +256,8 @@ const EventHandler = (() => {
} }
if (handlerKey.indexOf(namespace) > -1) { if (handlerKey.indexOf(namespace) > -1) {
removeHandler(element, events, typeEvent, storeElementEvent[handlerKey].originalHandler) const event = storeElementEvent[handlerKey]
removeHandler(element, events, typeEvent, event.originalHandler, event.delegationSelector)
} }
} }
} }
...@@ -262,33 +271,23 @@ const EventHandler = (() => { ...@@ -262,33 +271,23 @@ const EventHandler = (() => {
addHandler(element, event, handler, delegationFn, true) addHandler(element, event, handler, delegationFn, true)
}, },
off(element, originalTypeEvent, handler) { off(element, originalTypeEvent, handler, delegationFn) {
if (typeof originalTypeEvent !== 'string' || if (typeof originalTypeEvent !== 'string' || (typeof element === 'undefined' || element === null)) {
(typeof element === 'undefined' || element === null)) {
return return
} }
const events = getEvent(element) const [delegation, originalHandler, typeEvent] = normalizeParams(originalTypeEvent, handler, delegationFn)
let typeEvent = originalTypeEvent.replace(stripNameRegex, '')
const inNamespace = typeEvent !== originalTypeEvent const inNamespace = typeEvent !== originalTypeEvent
const custom = customEvents[typeEvent] const events = getEvent(element)
if (custom) {
typeEvent = custom
}
const isNative = nativeEvents.indexOf(typeEvent) > -1
if (!isNative) {
typeEvent = originalTypeEvent
}
if (typeof handler !== 'undefined') { if (typeof originalHandler !== 'undefined') {
// Simplest case: handler is passed, remove that listener ONLY. // Simplest case: handler is passed, remove that listener ONLY.
if (!events || !events[typeEvent]) { if (!events || !events[typeEvent]) {
return return
} }
removeHandler(element, events, typeEvent, handler) removeHandler(element, events, typeEvent, originalHandler, delegation ? handler : null)
return return
} }
...@@ -311,7 +310,8 @@ const EventHandler = (() => { ...@@ -311,7 +310,8 @@ const EventHandler = (() => {
const handlerKey = keyHandlers.replace(stripUidRegex, '') const handlerKey = keyHandlers.replace(stripUidRegex, '')
if (!inNamespace || originalTypeEvent.indexOf(handlerKey) > -1) { if (!inNamespace || originalTypeEvent.indexOf(handlerKey) > -1) {
removeHandler(element, events, typeEvent, storeElementEvent[keyHandlers].originalHandler) const event = storeElementEvent[keyHandlers]
removeHandler(element, events, typeEvent, event.originalHandler, event.delegationSelector)
} }
} }
}, },
......
...@@ -135,6 +135,32 @@ $(function () { ...@@ -135,6 +135,32 @@ $(function () {
document.body.removeChild(element) document.body.removeChild(element)
}) })
QUnit.test('on should add delegated event listener if delegated selector differs', function (assert) {
assert.expect(1)
var element = document.createElement('div')
var subelement = document.createElement('span')
element.appendChild(subelement)
var anchor = document.createElement('a')
element.appendChild(anchor)
var i = 0
var handler = function () {
i++
}
EventHandler.on(element, 'click', 'a', handler)
EventHandler.on(element, 'click', 'span', handler)
document.body.appendChild(element)
EventHandler.trigger(anchor, 'click')
EventHandler.trigger(subelement, 'click')
document.body.removeChild(element)
assert.ok(i === 2, 'listeners called')
})
QUnit.test('one should remove the listener after the event', function (assert) { QUnit.test('one should remove the listener after the event', function (assert) {
assert.expect(1) assert.expect(1)
...@@ -268,4 +294,48 @@ $(function () { ...@@ -268,4 +294,48 @@ $(function () {
EventHandler.trigger(element, 'foobar') EventHandler.trigger(element, 'foobar')
}) })
QUnit.test('off should remove the correct delegated event listener', function (assert) {
assert.expect(5)
var element = document.createElement('div')
var subelement = document.createElement('span')
element.appendChild(subelement)
var anchor = document.createElement('a')
element.appendChild(anchor)
var i = 0
var handler = function () {
i++
}
EventHandler.on(element, 'click', 'a', handler)
EventHandler.on(element, 'click', 'span', handler)
document.body.appendChild(element)
EventHandler.trigger(anchor, 'click')
EventHandler.trigger(subelement, 'click')
assert.ok(i === 2, 'first listeners called')
EventHandler.off(element, 'click', 'span', handler)
EventHandler.trigger(subelement, 'click')
assert.ok(i === 2, 'removed listener not called')
EventHandler.trigger(anchor, 'click')
assert.ok(i === 3, 'not removed listener called')
EventHandler.on(element, 'click', 'span', handler)
EventHandler.trigger(anchor, 'click')
EventHandler.trigger(subelement, 'click')
assert.ok(i === 5, 'listener re-registered')
EventHandler.off(element, 'click', 'span')
EventHandler.trigger(subelement, 'click')
assert.ok(i === 5, 'listener removed again')
document.body.removeChild(element)
})
}) })
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment