diff --git a/.bundlewatch.config.json b/.bundlewatch.config.json index 6f680664ca67..69b2900658d6 100644 --- a/.bundlewatch.config.json +++ b/.bundlewatch.config.json @@ -34,7 +34,7 @@ }, { "path": "./dist/js/bootstrap.bundle.js", - "maxSize": "43.0 kB" + "maxSize": "43.25 kB" }, { "path": "./dist/js/bootstrap.bundle.min.js", @@ -42,7 +42,7 @@ }, { "path": "./dist/js/bootstrap.esm.js", - "maxSize": "28.0 kB" + "maxSize": "28.25 kB" }, { "path": "./dist/js/bootstrap.esm.min.js", @@ -54,7 +54,7 @@ }, { "path": "./dist/js/bootstrap.min.js", - "maxSize": "16.25 kB" + "maxSize": "16.5 kB" } ], "ci": { diff --git a/js/src/dom/event-handler.js b/js/src/dom/event-handler.js index 561d8751d7ca..c1a21892752a 100644 --- a/js/src/dom/event-handler.js +++ b/js/src/dom/event-handler.js @@ -139,7 +139,7 @@ function normalizeParameters(originalTypeEvent, handler, delegationFunction) { return [isDelegated, callable, typeEvent] } -function addHandler(element, originalTypeEvent, handler, delegationFunction, oneOff) { +function addHandler(element, originalTypeEvent, handler, delegationFunction, options) { if (typeof originalTypeEvent !== 'string' || !element) { return } @@ -165,7 +165,7 @@ function addHandler(element, originalTypeEvent, handler, delegationFunction, one const previousFunction = findHandler(handlers, callable, isDelegated ? handler : null) if (previousFunction) { - previousFunction.oneOff = previousFunction.oneOff && oneOff + previousFunction.oneOff = previousFunction.oneOff && options.oneOff return } @@ -177,21 +177,22 @@ function addHandler(element, originalTypeEvent, handler, delegationFunction, one fn.delegationSelector = isDelegated ? handler : null fn.callable = callable - fn.oneOff = oneOff + fn.oneOff = options.oneOff fn.uidEvent = uid handlers[uid] = fn - element.addEventListener(typeEvent, fn, isDelegated) + element.addEventListener(typeEvent, fn, options?.capture ?? false) } -function removeHandler(element, events, typeEvent, handler, delegationSelector) { +// eslint-disable-next-line max-params +function removeHandler(element, events, typeEvent, handler, delegationSelector, options) { const fn = findHandler(events[typeEvent], handler, delegationSelector) if (!fn) { return } - element.removeEventListener(typeEvent, fn, Boolean(delegationSelector)) + element.removeEventListener(typeEvent, fn, options?.capture ?? false) delete events[typeEvent][fn.uidEvent] } @@ -212,15 +213,15 @@ function getTypeEvent(event) { } const EventHandler = { - on(element, event, handler, delegationFunction) { - addHandler(element, event, handler, delegationFunction, false) + on(element, event, handler, delegationFunction, options) { + addHandler(element, event, handler, delegationFunction, { ...options, oneOff: false }) }, - one(element, event, handler, delegationFunction) { - addHandler(element, event, handler, delegationFunction, true) + one(element, event, handler, delegationFunction, options) { + addHandler(element, event, handler, delegationFunction, { ...options, oneOff: true }) }, - off(element, originalTypeEvent, handler, delegationFunction) { + off(element, originalTypeEvent, handler, delegationFunction, options) { if (typeof originalTypeEvent !== 'string' || !element) { return } @@ -237,7 +238,7 @@ const EventHandler = { return } - removeHandler(element, events, typeEvent, callable, isDelegated ? handler : null) + removeHandler(element, events, typeEvent, callable, isDelegated ? handler : null, options) return } @@ -251,7 +252,7 @@ const EventHandler = { const handlerKey = keyHandlers.replace(stripUidRegex, '') if (!inNamespace || originalTypeEvent.includes(handlerKey)) { - removeHandler(element, events, typeEvent, event.callable, event.delegationSelector) + removeHandler(element, events, typeEvent, event.callable, event.delegationSelector, options) } } }, diff --git a/js/src/dropdown.js b/js/src/dropdown.js index 96094a3e6577..058428fc3e74 100644 --- a/js/src/dropdown.js +++ b/js/src/dropdown.js @@ -437,14 +437,14 @@ class Dropdown extends BaseComponent { * Data API implementation */ -EventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_DATA_TOGGLE, Dropdown.dataApiKeydownHandler) -EventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_MENU, Dropdown.dataApiKeydownHandler) -EventHandler.on(document, EVENT_CLICK_DATA_API, Dropdown.clearMenus) -EventHandler.on(document, EVENT_KEYUP_DATA_API, Dropdown.clearMenus) +EventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_DATA_TOGGLE, Dropdown.dataApiKeydownHandler, { capture: true }) +EventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_MENU, Dropdown.dataApiKeydownHandler, { capture: true }) +EventHandler.on(document, EVENT_CLICK_DATA_API, Dropdown.clearMenus, { capture: true }) +EventHandler.on(document, EVENT_KEYUP_DATA_API, Dropdown.clearMenus, { capture: true }) EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) { event.preventDefault() Dropdown.getOrCreateInstance(this).toggle() -}) +}, { capture: true }) /** * jQuery diff --git a/js/tests/unit/collapse.spec.js b/js/tests/unit/collapse.spec.js index 58c5367526b9..43f70653e787 100644 --- a/js/tests/unit/collapse.spec.js +++ b/js/tests/unit/collapse.spec.js @@ -514,28 +514,20 @@ describe('Collapse', () => { describe('data-api', () => { it('should prevent url change if click on nested elements', () => { - return new Promise(resolve => { - fixtureEl.innerHTML = [ - '', - '
' - ].join('') + fixtureEl.innerHTML = [ + '', + '
' + ].join('') - const triggerEl = fixtureEl.querySelector('a') - const nestedTriggerEl = fixtureEl.querySelector('#nested') + const nestedTriggerEl = fixtureEl.querySelector('#nested') - const spy = spyOn(Event.prototype, 'preventDefault').and.callThrough() + const spy = spyOn(Event.prototype, 'preventDefault').and.callThrough() - triggerEl.addEventListener('click', event => { - expect(event.target.isEqualNode(nestedTriggerEl)).toBeTrue() - expect(event.delegateTarget.isEqualNode(triggerEl)).toBeTrue() - expect(spy).toHaveBeenCalled() - resolve() - }) + nestedTriggerEl.click() - nestedTriggerEl.click() - }) + expect(spy).toHaveBeenCalled() }) it('should show multiple collapsed elements', () => { diff --git a/js/tests/unit/dropdown.spec.js b/js/tests/unit/dropdown.spec.js index 63ae4bd102bc..9d94268756f2 100644 --- a/js/tests/unit/dropdown.spec.js +++ b/js/tests/unit/dropdown.spec.js @@ -1361,7 +1361,7 @@ describe('Dropdown', () => { btnDropdown.addEventListener('shown.bs.dropdown', () => { expect(btnDropdown).toHaveClass('show') - const keyup = createEvent('keyup') + const keyup = createEvent('keyup', { bubbles: true }) keyup.key = 'Tab' document.dispatchEvent(keyup) @@ -1456,7 +1456,7 @@ describe('Dropdown', () => { expect(triggerDropdownFirst).toHaveClass('show') expect(fixtureEl.querySelectorAll('.dropdown-menu.show')).toHaveSize(1) - const keyup = createEvent('keyup') + const keyup = createEvent('keyup', { bubbles: true }) keyup.key = 'Tab' document.dispatchEvent(keyup) @@ -1471,7 +1471,7 @@ describe('Dropdown', () => { expect(triggerDropdownLast).toHaveClass('show') expect(fixtureEl.querySelectorAll('.dropdown-menu.show')).toHaveSize(1) - const keyup = createEvent('keyup') + const keyup = createEvent('keyup', { bubbles: true }) keyup.key = 'Tab' document.dispatchEvent(keyup) @@ -1570,7 +1570,7 @@ describe('Dropdown', () => { }) triggerDropdown.addEventListener('shown.bs.dropdown', () => { - const keydown = createEvent('keydown') + const keydown = createEvent('keydown', { bubbles: true }) keydown.key = 'Escape' triggerDropdown.dispatchEvent(keydown) @@ -1637,7 +1637,7 @@ describe('Dropdown', () => { triggerDropdown.addEventListener('shown.bs.dropdown', () => { input.focus() - const keydown = createEvent('keydown') + const keydown = createEvent('keydown', { bubbles: true }) keydown.key = 'ArrowUp' input.dispatchEvent(keydown) @@ -1671,7 +1671,7 @@ describe('Dropdown', () => { const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]') triggerDropdown.addEventListener('shown.bs.dropdown', () => { - const keydown = createEvent('keydown') + const keydown = createEvent('keydown', { bubbles: true }) keydown.key = 'ArrowDown' triggerDropdown.dispatchEvent(keydown) @@ -1708,7 +1708,7 @@ describe('Dropdown', () => { const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]') triggerDropdown.addEventListener('shown.bs.dropdown', () => { - const keydown = createEvent('keydown') + const keydown = createEvent('keydown', { bubbles: true }) keydown.key = 'ArrowDown' triggerDropdown.dispatchEvent(keydown) @@ -1741,7 +1741,7 @@ describe('Dropdown', () => { const item2 = fixtureEl.querySelector('#item2') triggerDropdown.addEventListener('shown.bs.dropdown', () => { - const keydownArrowDown = createEvent('keydown') + const keydownArrowDown = createEvent('keydown', { bubbles: true }) keydownArrowDown.key = 'ArrowDown' triggerDropdown.dispatchEvent(keydownArrowDown) @@ -1750,7 +1750,7 @@ describe('Dropdown', () => { document.activeElement.dispatchEvent(keydownArrowDown) expect(document.activeElement).toEqual(item2, 'item2 is focused') - const keydownArrowUp = createEvent('keydown') + const keydownArrowUp = createEvent('keydown', { bubbles: true }) keydownArrowUp.key = 'ArrowUp' document.activeElement.dispatchEvent(keydownArrowUp) @@ -1785,7 +1785,7 @@ describe('Dropdown', () => { }) }) - const keydown = createEvent('keydown') + const keydown = createEvent('keydown', { bubbles: true }) keydown.key = 'ArrowUp' triggerDropdown.dispatchEvent(keydown) }) @@ -1813,7 +1813,7 @@ describe('Dropdown', () => { }) }) - const keydown = createEvent('keydown') + const keydown = createEvent('keydown', { bubbles: true }) keydown.key = 'ArrowDown' triggerDropdown.dispatchEvent(keydown) }) @@ -1840,7 +1840,7 @@ describe('Dropdown', () => { triggerDropdown.addEventListener('shown.bs.dropdown', () => { expect(triggerDropdown).toHaveClass('show') - input.dispatchEvent(createEvent('click')) + input.dispatchEvent(createEvent('click', { bubbles: true })) }) triggerDropdown.click() @@ -1868,7 +1868,7 @@ describe('Dropdown', () => { triggerDropdown.addEventListener('shown.bs.dropdown', () => { expect(triggerDropdown).toHaveClass('show') - textarea.dispatchEvent(createEvent('click')) + textarea.dispatchEvent(createEvent('click', { bubbles: true })) }) triggerDropdown.click() @@ -1895,9 +1895,7 @@ describe('Dropdown', () => { }) triggerDropdown.addEventListener('shown.bs.dropdown', () => { - input.dispatchEvent(createEvent('click', { - bubbles: true - })) + input.dispatchEvent(createEvent('click', { bubbles: true })) }) triggerDropdown.click() @@ -1922,14 +1920,14 @@ describe('Dropdown', () => { const textarea = fixtureEl.querySelector('textarea') const test = (eventKey, elementToDispatch) => { - const event = createEvent('keydown') + const event = createEvent('keydown', { bubbles: true }) event.key = eventKey elementToDispatch.focus() elementToDispatch.dispatchEvent(event) expect(document.activeElement).toEqual(elementToDispatch, `${elementToDispatch.tagName} still focused`) } - const keydownEscape = createEvent('keydown') + const keydownEscape = createEvent('keydown', { bubbles: true }) keydownEscape.key = 'Escape' triggerDropdown.addEventListener('shown.bs.dropdown', () => { @@ -1985,7 +1983,7 @@ describe('Dropdown', () => { // Key escape button.focus() // Key escape - const keydownEscape = createEvent('keydown') + const keydownEscape = createEvent('keydown', { bubbles: true }) keydownEscape.key = 'Escape' button.dispatchEvent(keydownEscape) @@ -2351,10 +2349,10 @@ describe('Dropdown', () => { const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]') const dropdown = fixtureEl.querySelector('.dropdown') - const keydown = createEvent('keydown') + const keydown = createEvent('keydown', { bubbles: true }) keydown.key = 'ArrowDown' - const keyup = createEvent('keyup') + const keyup = createEvent('keyup', { bubbles: true }) keyup.key = 'ArrowUp' const handleArrowDown = () => { diff --git a/js/tests/unit/toast.spec.js b/js/tests/unit/toast.spec.js index 200fe3e40cf7..a6039cce57f8 100644 --- a/js/tests/unit/toast.spec.js +++ b/js/tests/unit/toast.spec.js @@ -246,7 +246,7 @@ describe('Toast', () => { resolve() }) - const mouseOverEvent = createEvent('mouseover') + const mouseOverEvent = createEvent('mouseover', { bubbles: true }) toastEl.dispatchEvent(mouseOverEvent) }, toast._config.delay / 2) @@ -309,7 +309,7 @@ describe('Toast', () => { }) toastEl.addEventListener('focusin', () => { - const mouseOutEvent = createEvent('mouseout') + const mouseOutEvent = createEvent('mouseout', { bubbles: true }) toastEl.dispatchEvent(mouseOutEvent) }) @@ -323,7 +323,7 @@ describe('Toast', () => { resolve() }) - const mouseOverEvent = createEvent('mouseover') + const mouseOverEvent = createEvent('mouseover', { bubbles: true }) toastEl.dispatchEvent(mouseOverEvent) }, toast._config.delay / 2) @@ -362,7 +362,7 @@ describe('Toast', () => { resolve() }) - const mouseOverEvent = createEvent('mouseover') + const mouseOverEvent = createEvent('mouseover', { bubbles: true }) toastEl.dispatchEvent(mouseOverEvent) }, toast._config.delay / 2) @@ -392,7 +392,7 @@ describe('Toast', () => { }) toastEl.addEventListener('focusin', () => { - const mouseOutEvent = createEvent('mouseout') + const mouseOutEvent = createEvent('mouseout', { bubbles: true }) toastEl.dispatchEvent(mouseOutEvent) }) @@ -401,7 +401,7 @@ describe('Toast', () => { resolve() }) - const mouseOverEvent = createEvent('mouseover') + const mouseOverEvent = createEvent('mouseover', { bubbles: true }) toastEl.dispatchEvent(mouseOverEvent) }, toast._config.delay / 2) diff --git a/js/tests/unit/util/component-functions.spec.js b/js/tests/unit/util/component-functions.spec.js index ce83785e2371..1441d9b30c00 100644 --- a/js/tests/unit/util/component-functions.spec.js +++ b/js/tests/unit/util/component-functions.spec.js @@ -39,7 +39,7 @@ describe('Plugin functions', () => { const spyTest = spyOn(DummyClass2.prototype, 'testMethod') const componentWrapper = fixtureEl.querySelector('#foo') const btnClose = fixtureEl.querySelector('[data-bs-dismiss="test"]') - const event = createEvent('click') + const event = createEvent('click', { bubbles: true }) enableDismissTrigger(DummyClass2, 'testMethod') btnClose.dispatchEvent(event) @@ -59,7 +59,7 @@ describe('Plugin functions', () => { const spyHide = spyOn(DummyClass2.prototype, 'hide') const componentWrapper = fixtureEl.querySelector('#foo') const btnClose = fixtureEl.querySelector('[data-bs-dismiss="test"]') - const event = createEvent('click') + const event = createEvent('click', { bubbles: true }) enableDismissTrigger(DummyClass2) btnClose.dispatchEvent(event) @@ -77,7 +77,7 @@ describe('Plugin functions', () => { const spy = spyOn(DummyClass2, 'getOrCreateInstance').and.callThrough() const btnClose = fixtureEl.querySelector('[data-bs-dismiss="test"]') - const event = createEvent('click') + const event = createEvent('click', { bubbles: true }) enableDismissTrigger(DummyClass2) btnClose.dispatchEvent(event) @@ -93,7 +93,7 @@ describe('Plugin functions', () => { ].join('') const btnClose = fixtureEl.querySelector('[data-bs-dismiss="test"]') - const event = createEvent('click') + const event = createEvent('click', { bubbles: true }) enableDismissTrigger(DummyClass2) const spy = spyOn(Event.prototype, 'preventDefault').and.callThrough() diff --git a/js/tests/unit/util/focustrap.spec.js b/js/tests/unit/util/focustrap.spec.js index 0a20017d598b..a57cc388c880 100644 --- a/js/tests/unit/util/focustrap.spec.js +++ b/js/tests/unit/util/focustrap.spec.js @@ -107,7 +107,7 @@ describe('FocusTrap', () => { first.addEventListener('focusin', focusInListener) - const keydown = createEvent('keydown') + const keydown = createEvent('keydown', { bubbles: true }) keydown.key = 'Tab' document.dispatchEvent(keydown) @@ -146,7 +146,7 @@ describe('FocusTrap', () => { last.addEventListener('focusin', focusInListener) - const keydown = createEvent('keydown') + const keydown = createEvent('keydown', { bubbles: true }) keydown.key = 'Tab' keydown.shiftKey = true