From 33211eefdfb27eff7ba21886e16f2efdc0efa3e6 Mon Sep 17 00:00:00 2001 From: Alessandro Chitolina Date: Fri, 15 Sep 2017 16:07:24 +0200 Subject: Rewritten modal without jquery (#23955) * Trigger jquery events if available in event handler * Rewritten modal without jquery --- js/src/dom/data.js | 2 +- js/src/dom/eventHandler.js | 105 ++++++++++++++++++++++++++++++++++----------- js/src/dom/manipulator.js | 20 +++++++++ 3 files changed, 102 insertions(+), 25 deletions(-) (limited to 'js/src/dom') diff --git a/js/src/dom/data.js b/js/src/dom/data.js index 655706fbc..68908d8f2 100644 --- a/js/src/dom/data.js +++ b/js/src/dom/data.js @@ -21,7 +21,7 @@ const mapData = (() => { id++ }, get(element, key) { - if (typeof element.key === 'undefined') { + if (typeof element === 'undefined' || typeof element.key === 'undefined') { return null } diff --git a/js/src/dom/eventHandler.js b/js/src/dom/eventHandler.js index 746f84bcb..a69ab6136 100644 --- a/js/src/dom/eventHandler.js +++ b/js/src/dom/eventHandler.js @@ -63,6 +63,7 @@ if (!window.Event || typeof window.Event !== 'function') { const namespaceRegex = /[^.]*(?=\..*)\.|.*/ const stripNameRegex = /\..*/ const keyEventRegex = /^key/ +const stripUidRegex = /::\d+$/ // Events storage const eventRegistry = {} @@ -110,10 +111,10 @@ function bootstrapHandler(element, fn) { } } -function bootstrapDelegationHandler(selector, fn) { +function bootstrapDelegationHandler(element, selector, fn) { return function (event) { event = fixEvent(event) - const domElements = document.querySelectorAll(selector) + const domElements = element.querySelectorAll(selector) for (let target = event.target; target && target !== this; target = target.parentNode) { for (let i = domElements.length; i--;) { if (domElements[i] === target) { @@ -126,6 +127,26 @@ function bootstrapDelegationHandler(selector, fn) { } } +function removeHandler(element, events, typeEvent, handler) { + const uidEvent = handler.uidEvent + const fn = events[typeEvent][uidEvent] + element.removeEventListener(typeEvent, fn, fn.delegation) + delete events[typeEvent][uidEvent] +} + +function removeNamespacedHandlers(element, events, typeEvent, namespace) { + const storeElementEvent = events[typeEvent] || {} + for (const handlerKey in storeElementEvent) { + if (!Object.prototype.hasOwnProperty.call(storeElementEvent, handlerKey)) { + continue + } + + if (handlerKey.indexOf(namespace) > -1) { + removeHandler(element, events, typeEvent, storeElementEvent[handlerKey].originalHandler) + } + } +} + const EventHandler = { on(element, originalTypeEvent, handler, delegationFn) { if (typeof originalTypeEvent !== 'string' || @@ -155,7 +176,7 @@ const EventHandler = { return } - const fn = !delegation ? bootstrapHandler(element, handler) : bootstrapDelegationHandler(handler, delegationFn) + const fn = !delegation ? bootstrapHandler(element, handler) : bootstrapDelegationHandler(element, handler, delegationFn) fn.isDelegation = delegation handlers[uid] = fn originalHandler.uidEvent = uid @@ -179,43 +200,49 @@ const EventHandler = { const events = getEvent(element) let typeEvent = originalTypeEvent.replace(stripNameRegex, '') + const inNamespace = typeEvent !== originalTypeEvent const custom = customEvents[typeEvent] if (custom) { typeEvent = custom } + const isNative = nativeEvents.indexOf(typeEvent) > -1 if (!isNative) { typeEvent = originalTypeEvent } - if (typeof handler === 'undefined') { + if (typeof handler !== 'undefined') { + // Simplest case: handler is passed, remove that listener ONLY. + if (!events || !events[typeEvent]) { + return + } + + removeHandler(element, events, typeEvent, handler) + return + } + + const isNamespace = originalTypeEvent.charAt(0) === '.' + if (isNamespace) { for (const elementEvent in events) { if (!Object.prototype.hasOwnProperty.call(events, elementEvent)) { continue } - const storeElementEvent = events[elementEvent] - for (const keyHandlers in storeElementEvent) { - if (!Object.prototype.hasOwnProperty.call(storeElementEvent, keyHandlers)) { - continue - } - // delete all the namespaced listeners - if (inNamespace && keyHandlers.indexOf(originalTypeEvent) > -1) { - const handlerFn = events[elementEvent][keyHandlers] - EventHandler.off(element, elementEvent, handlerFn.originalHandler) - } - } + removeNamespacedHandlers(element, events, elementEvent, originalTypeEvent.substr(1)) } - } else { - if (!events || !events[typeEvent]) { - return + } + + const storeElementEvent = events[typeEvent] || {} + for (const keyHandlers in storeElementEvent) { + if (!Object.prototype.hasOwnProperty.call(storeElementEvent, keyHandlers)) { + continue } - const uidEvent = handler.uidEvent - const fn = events[typeEvent][uidEvent] - element.removeEventListener(typeEvent, fn, fn.delegation) - delete events[typeEvent][uidEvent] + const handlerKey = keyHandlers.replace(stripUidRegex, '') + if (!inNamespace || originalTypeEvent.indexOf(handlerKey) > -1) { + removeHandler(element, events, typeEvent, storeElementEvent[keyHandlers].originalHandler) + } } }, @@ -226,7 +253,25 @@ const EventHandler = { } const typeEvent = event.replace(stripNameRegex, '') + const inNamespace = event !== typeEvent const isNative = nativeEvents.indexOf(typeEvent) > -1 + + const $ = Util.jQuery + let jQueryEvent + + let bubbles = true + let nativeDispatch = true + let defaultPrevented = false + + if (inNamespace && typeof $ !== 'undefined') { + jQueryEvent = new $.Event(event, args) + + $(element).trigger(jQueryEvent) + bubbles = !jQueryEvent.isPropagationStopped() + nativeDispatch = !jQueryEvent.isImmediatePropagationStopped() + defaultPrevented = jQueryEvent.isDefaultPrevented() + } + let evt = null if (isNative) { @@ -234,7 +279,7 @@ const EventHandler = { evt.initEvent(typeEvent, true, true) } else { evt = new CustomEvent(event, { - bubbles: true, + bubbles, cancelable: true }) } @@ -243,7 +288,19 @@ const EventHandler = { if (typeof args !== 'undefined') { evt = Util.extend(evt, args) } - element.dispatchEvent(evt) + + if (defaultPrevented) { + evt.preventDefault() + } + + if (nativeDispatch) { + element.dispatchEvent(evt) + } + + if (evt.defaultPrevented && typeof jQueryEvent !== 'undefined') { + jQueryEvent.preventDefault() + } + return evt } } diff --git a/js/src/dom/manipulator.js b/js/src/dom/manipulator.js index c12480234..b8136dda1 100644 --- a/js/src/dom/manipulator.js +++ b/js/src/dom/manipulator.js @@ -1,3 +1,5 @@ +import Util from '../util' + /** * -------------------------------------------------------------------------- * Bootstrap (v4.0.0-beta): dom/manipulator.js @@ -18,6 +20,24 @@ const Manipulator = { return input.bsChecked || input.checked } throw new Error('INPUT parameter is not an HTMLInputElement') + }, + + setDataAttribute(element, key, value) { + const $ = Util.jQuery + if (typeof $ !== 'undefined') { + $(element).data(key, value) + } + + element.setAttribute(`data-${key.replace(/[A-Z]/g, (chr) => `-${chr.toLowerCase()}`)}`, value) + }, + + removeDataAttribute(element, key) { + const $ = Util.jQuery + if (typeof $ !== 'undefined') { + $(element).removeData(key) + } + + element.removeAttribute(`data-${key.replace(/[A-Z]/g, (chr) => `-${chr.toLowerCase()}`)}`) } } -- cgit v1.2.3