diff --git a/js/src/dom/selector-engine.js b/js/src/dom/selector-engine.js index a4d81f3b9127..78db38fdd4e1 100644 --- a/js/src/dom/selector-engine.js +++ b/js/src/dom/selector-engine.js @@ -57,6 +57,10 @@ const SelectorEngine = { return parents }, + closest(element, selector) { + return Element.prototype.closest.call(element, selector) + }, + prev(element, selector) { let previous = element.previousElementSibling diff --git a/js/src/dropdown.js b/js/src/dropdown.js index 96094a3e6577..95e9797b4573 100644 --- a/js/src/dropdown.js +++ b/js/src/dropdown.js @@ -95,7 +95,8 @@ class Dropdown extends BaseComponent { super(element, config) this._popper = null - this._parent = this._element.parentNode // dropdown wrapper + const wrapperSelector = `:has(${SELECTOR_MENU})` + this._parent = SelectorEngine.closest(element, wrapperSelector) // dropdown wrapper // TODO: v6 revert #37011 & change markup https://getbootstrap.com/docs/5.3/forms/input-group/ this._menu = SelectorEngine.next(this._element, SELECTOR_MENU)[0] || SelectorEngine.prev(this._element, SELECTOR_MENU)[0] || diff --git a/js/tests/unit/dom/selector-engine.spec.js b/js/tests/unit/dom/selector-engine.spec.js index 95d9bf8ec9d8..aee5daff5be0 100644 --- a/js/tests/unit/dom/selector-engine.spec.js +++ b/js/tests/unit/dom/selector-engine.spec.js @@ -81,6 +81,28 @@ describe('SelectorEngine', () => { }) }) + describe('closest', () => { + it('should return one element when element with selector is the direct parent', () => { + const testId = 'test' + fixtureEl.innerHTML = `
` + + const element = fixtureEl.querySelector('#element') + const parent = fixtureEl.querySelector(`#${testId}`) + + expect(SelectorEngine.closest(element, `#${testId}`)).toEqual(parent) + }) + + it('should return one element when element with selector is an ancestor', () => { + const testId = 'test' + fixtureEl.innerHTML = `
` + + const element = fixtureEl.querySelector('#element') + const ancestor = fixtureEl.querySelector(`#${testId}`) + + expect(SelectorEngine.closest(element, `#${testId}`)).toEqual(ancestor) + }) + }) + describe('prev', () => { it('should return previous element', () => { fixtureEl.innerHTML = '
'