diff options
| author | Bobby <[email protected]> | 2024-08-16 20:47:33 -0400 |
|---|---|---|
| committer | GitHub <[email protected]> | 2024-08-16 20:47:33 -0400 |
| commit | 6b28433d9cfde435be8ec2bd6cf91e6324d08865 (patch) | |
| tree | 8343c27b8b95ff5639233e81cf157f92e5688466 /js/src/dom/event-handler.js | |
| parent | d53094ec16ba385faae2973ddee648698b32ab24 (diff) | |
| parent | 048f56f51460df75e92a2f7b472e1c56baeb68f7 (diff) | |
| download | bootstrap-main.tar.xz bootstrap-main.zip | |
Diffstat (limited to 'js/src/dom/event-handler.js')
| -rw-r--r-- | js/src/dom/event-handler.js | 173 |
1 files changed, 76 insertions, 97 deletions
diff --git a/js/src/dom/event-handler.js b/js/src/dom/event-handler.js index b9ebce324..561d8751d 100644 --- a/js/src/dom/event-handler.js +++ b/js/src/dom/event-handler.js @@ -1,11 +1,11 @@ /** * -------------------------------------------------------------------------- - * Bootstrap (v5.1.3): dom/event-handler.js + * Bootstrap dom/event-handler.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ -import { getjQuery } from '../util/index' +import { getjQuery } from '../util/index.js' /** * Constants @@ -20,7 +20,7 @@ const customEvents = { mouseenter: 'mouseover', mouseleave: 'mouseout' } -const customEventsRegex = /^(mouseenter|mouseleave)/i + const nativeEvents = new Set([ 'click', 'dblclick', @@ -74,12 +74,12 @@ const nativeEvents = new Set([ * Private methods */ -function getUidEvent(element, uid) { +function makeEventUid(element, uid) { return (uid && `${uid}::${uidEvent++}`) || element.uidEvent || uidEvent++ } -function getEvent(element) { - const uid = getUidEvent(element) +function getElementEvents(element) { + const uid = makeEventUid(element) element.uidEvent = uid eventRegistry[uid] = eventRegistry[uid] || {} @@ -89,7 +89,7 @@ function getEvent(element) { function bootstrapHandler(element, fn) { return function handler(event) { - event.delegateTarget = element + hydrateObj(event, { delegateTarget: element }) if (handler.oneOff) { EventHandler.off(element, event.type, fn) @@ -104,65 +104,52 @@ function bootstrapDelegationHandler(element, selector, fn) { const domElements = element.querySelectorAll(selector) for (let { target } = event; target && target !== this; target = target.parentNode) { - for (let i = domElements.length; i--;) { - if (domElements[i] === target) { - event.delegateTarget = target + for (const domElement of domElements) { + if (domElement !== target) { + continue + } - if (handler.oneOff) { - EventHandler.off(element, event.type, selector, fn) - } + hydrateObj(event, { delegateTarget: target }) - return fn.apply(target, [event]) + if (handler.oneOff) { + EventHandler.off(element, event.type, selector, fn) } + + return fn.apply(target, [event]) } } - - // To please ESLint - return null } } -function findHandler(events, handler, delegationSelector = null) { - const uidEventList = Object.keys(events) - - for (const uidEvent of uidEventList) { - const event = events[uidEvent] - - if (event.originalHandler === handler && event.delegationSelector === delegationSelector) { - return event - } - } - - return null +function findHandler(events, callable, delegationSelector = null) { + return Object.values(events) + .find(event => event.callable === callable && event.delegationSelector === delegationSelector) } -function normalizeParams(originalTypeEvent, handler, delegationFn) { - const delegation = typeof handler === 'string' - const originalHandler = delegation ? delegationFn : handler +function normalizeParameters(originalTypeEvent, handler, delegationFunction) { + const isDelegated = typeof handler === 'string' + // TODO: tooltip passes `false` instead of selector, so we need to check + const callable = isDelegated ? delegationFunction : (handler || delegationFunction) let typeEvent = getTypeEvent(originalTypeEvent) - const isNative = nativeEvents.has(typeEvent) - if (!isNative) { + if (!nativeEvents.has(typeEvent)) { typeEvent = originalTypeEvent } - return [delegation, originalHandler, typeEvent] + return [isDelegated, callable, typeEvent] } -function addHandler(element, originalTypeEvent, handler, delegationFn, oneOff) { +function addHandler(element, originalTypeEvent, handler, delegationFunction, oneOff) { if (typeof originalTypeEvent !== 'string' || !element) { return } - if (!handler) { - handler = delegationFn - delegationFn = null - } + let [isDelegated, callable, typeEvent] = normalizeParameters(originalTypeEvent, handler, delegationFunction) // in case of mouseenter or mouseleave wrap the handler within a function that checks for its DOM position // this prevents the handler from being dispatched the same way as mouseover or mouseout does - if (customEventsRegex.test(originalTypeEvent)) { - const wrapFn = fn => { + if (originalTypeEvent in customEvents) { + const wrapFunction = fn => { return function (event) { if (!event.relatedTarget || (event.relatedTarget !== event.delegateTarget && !event.delegateTarget.contains(event.relatedTarget))) { return fn.call(this, event) @@ -170,36 +157,31 @@ function addHandler(element, originalTypeEvent, handler, delegationFn, oneOff) { } } - if (delegationFn) { - delegationFn = wrapFn(delegationFn) - } else { - handler = wrapFn(handler) - } + callable = wrapFunction(callable) } - const [delegation, originalHandler, typeEvent] = normalizeParams(originalTypeEvent, handler, delegationFn) - const events = getEvent(element) + const events = getElementEvents(element) const handlers = events[typeEvent] || (events[typeEvent] = {}) - const previousFn = findHandler(handlers, originalHandler, delegation ? handler : null) + const previousFunction = findHandler(handlers, callable, isDelegated ? handler : null) - if (previousFn) { - previousFn.oneOff = previousFn.oneOff && oneOff + if (previousFunction) { + previousFunction.oneOff = previousFunction.oneOff && oneOff return } - const uid = getUidEvent(originalHandler, originalTypeEvent.replace(namespaceRegex, '')) - const fn = delegation ? - bootstrapDelegationHandler(element, handler, delegationFn) : - bootstrapHandler(element, handler) + const uid = makeEventUid(callable, originalTypeEvent.replace(namespaceRegex, '')) + const fn = isDelegated ? + bootstrapDelegationHandler(element, handler, callable) : + bootstrapHandler(element, callable) - fn.delegationSelector = delegation ? handler : null - fn.originalHandler = originalHandler + fn.delegationSelector = isDelegated ? handler : null + fn.callable = callable fn.oneOff = oneOff fn.uidEvent = uid handlers[uid] = fn - element.addEventListener(typeEvent, fn, delegation) + element.addEventListener(typeEvent, fn, isDelegated) } function removeHandler(element, events, typeEvent, handler, delegationSelector) { @@ -216,10 +198,9 @@ function removeHandler(element, events, typeEvent, handler, delegationSelector) function removeNamespacedHandlers(element, events, typeEvent, namespace) { const storeElementEvent = events[typeEvent] || {} - for (const handlerKey of Object.keys(storeElementEvent)) { + for (const [handlerKey, event] of Object.entries(storeElementEvent)) { if (handlerKey.includes(namespace)) { - const event = storeElementEvent[handlerKey] - removeHandler(element, events, typeEvent, event.originalHandler, event.delegationSelector) + removeHandler(element, events, typeEvent, event.callable, event.delegationSelector) } } } @@ -231,31 +212,32 @@ function getTypeEvent(event) { } const EventHandler = { - on(element, event, handler, delegationFn) { - addHandler(element, event, handler, delegationFn, false) + on(element, event, handler, delegationFunction) { + addHandler(element, event, handler, delegationFunction, false) }, - one(element, event, handler, delegationFn) { - addHandler(element, event, handler, delegationFn, true) + one(element, event, handler, delegationFunction) { + addHandler(element, event, handler, delegationFunction, true) }, - off(element, originalTypeEvent, handler, delegationFn) { + off(element, originalTypeEvent, handler, delegationFunction) { if (typeof originalTypeEvent !== 'string' || !element) { return } - const [delegation, originalHandler, typeEvent] = normalizeParams(originalTypeEvent, handler, delegationFn) + const [isDelegated, callable, typeEvent] = normalizeParameters(originalTypeEvent, handler, delegationFunction) const inNamespace = typeEvent !== originalTypeEvent - const events = getEvent(element) + const events = getElementEvents(element) + const storeElementEvent = events[typeEvent] || {} const isNamespace = originalTypeEvent.startsWith('.') - if (typeof originalHandler !== 'undefined') { + if (typeof callable !== 'undefined') { // Simplest case: handler is passed, remove that listener ONLY. - if (!events || !events[typeEvent]) { + if (!Object.keys(storeElementEvent).length) { return } - removeHandler(element, events, typeEvent, originalHandler, delegation ? handler : null) + removeHandler(element, events, typeEvent, callable, isDelegated ? handler : null) return } @@ -265,13 +247,11 @@ const EventHandler = { } } - const storeElementEvent = events[typeEvent] || {} - for (const keyHandlers of Object.keys(storeElementEvent)) { + for (const [keyHandlers, event] of Object.entries(storeElementEvent)) { const handlerKey = keyHandlers.replace(stripUidRegex, '') if (!inNamespace || originalTypeEvent.includes(handlerKey)) { - const event = storeElementEvent[keyHandlers] - removeHandler(element, events, typeEvent, event.originalHandler, event.delegationSelector) + removeHandler(element, events, typeEvent, event.callable, event.delegationSelector) } } }, @@ -284,13 +264,11 @@ const EventHandler = { const $ = getjQuery() const typeEvent = getTypeEvent(event) const inNamespace = event !== typeEvent - const isNative = nativeEvents.has(typeEvent) - let jQueryEvent + let jQueryEvent = null let bubbles = true let nativeDispatch = true let defaultPrevented = false - let evt = null if (inNamespace && $) { jQueryEvent = $.Event(event, args) @@ -301,23 +279,7 @@ const EventHandler = { defaultPrevented = jQueryEvent.isDefaultPrevented() } - if (isNative) { - evt = document.createEvent('HTMLEvents') - evt.initEvent(typeEvent, bubbles, true) - } else { - evt = new CustomEvent(event, { bubbles, cancelable: true }) - } - - // merge custom information in our event - if (typeof args !== 'undefined') { - for (const key of Object.keys(args)) { - Object.defineProperty(evt, key, { - get() { - return args[key] - } - }) - } - } + const evt = hydrateObj(new Event(event, { bubbles, cancelable: true }), args) if (defaultPrevented) { evt.preventDefault() @@ -327,7 +289,7 @@ const EventHandler = { element.dispatchEvent(evt) } - if (evt.defaultPrevented && typeof jQueryEvent !== 'undefined') { + if (evt.defaultPrevented && jQueryEvent) { jQueryEvent.preventDefault() } @@ -335,4 +297,21 @@ const EventHandler = { } } +function hydrateObj(obj, meta = {}) { + for (const [key, value] of Object.entries(meta)) { + try { + obj[key] = value + } catch { + Object.defineProperty(obj, key, { + configurable: true, + get() { + return value + } + }) + } + } + + return obj +} + export default EventHandler |
