-
-
Notifications
You must be signed in to change notification settings - Fork 79.2k
Description
Prerequisites
- I have searched for duplicate or closed feature requests
- I have read the contributing guidelines
Proposal
Hello Bootstrap team,
I have a suggestion to simplify and potentially optimize the bootstrapDelegationHandler
function in event-handler.js
.
Current Behavior
bootstrap/js/src/dom/event-handler.js
Lines 102 to 122 in 0cbfe13
function bootstrapDelegationHandler(element, selector, fn) { | |
return function handler(event) { | |
const domElements = element.querySelectorAll(selector) | |
for (let { target } = event; target && target !== this; target = target.parentNode) { | |
for (const domElement of domElements) { | |
if (domElement !== target) { | |
continue | |
} | |
hydrateObj(event, { delegateTarget: target }) | |
if (handler.oneOff) { | |
EventHandler.off(element, event.type, selector, fn) | |
} | |
return fn.apply(target, [event]) | |
} | |
} | |
} | |
} |
The bootstrapDelegationHandler
function is invoked when the third argument of the EventHandler.on
method, handler
, is a selector string. It is widely used to detect components like carousel
, collapse
, dropdown
, modal
, offcanvas
, and tab
based on their data attributes. In these cases, the first argument of bootstrapDelegationHandler
, element
, is typically set to document
.
For example:
Line 444 in 0cbfe13
EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) { |
As a result, Bootstrap executes multiple DOM selector queries against the entire DOM tree every time an event is triggered to check if the event is related to a specific component. For instance, with click events in the default bundle (loading all components), this can result in 11 DOM queries, causing noticeable delays depending on the DOM tree size and the user's device.
Motivation and context
Proposed Implementation
I suggest replacing the current DOM traversal logic with the more efficient Element.closest
method, which simplifies the implementation and improves performance. Here's the proposed code:
function bootstrapDelegationHandler(element, selector, fn) {
return function handler(event) {
const target = event.target.closest(selector);
if (target && element.contains(target)) {
hydrateObj(event, { delegateTarget: target });
if (handler.oneOff) {
EventHandler.off(element, event.type, selector, fn);
}
return fn.apply(target, [event]);
}
};
}
The Element.closest
method is widely supported in modern browsers, aligning with Bootstrap's browser compatibility requirements.
https://caniuse.com/element-closest
If there are any potential side effects or edge cases I might have overlooked, I would greatly appreciate your feedback.