diff --git a/js/src/dropdown.js b/js/src/dropdown.js index af9c54faa92c..dfbf86f929dd 100644 --- a/js/src/dropdown.js +++ b/js/src/dropdown.js @@ -218,6 +218,13 @@ class Dropdown extends BaseComponent { return } + // If this is a touch-enabled device we remove the extra + // empty mouseover listeners we added for iOS support + if ('ontouchstart' in document.documentElement) { + [].concat(...document.body.children) + .forEach(elem => EventHandler.off(elem, 'mouseover', null, noop())) + } + if (this._popper) { this._popper.destroy() } diff --git a/js/tests/unit/dropdown.spec.js b/js/tests/unit/dropdown.spec.js index d33a065b3748..e7580e633584 100644 --- a/js/tests/unit/dropdown.spec.js +++ b/js/tests/unit/dropdown.spec.js @@ -895,6 +895,39 @@ describe('Dropdown', () => { done() }) }) + + it('should remove event listener on touch-enabled device that was added in show method', done => { + fixtureEl.innerHTML = [ + '' + ].join('') + + const defaultValueOnTouchStart = document.documentElement.ontouchstart + const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]') + const dropdown = new Dropdown(btnDropdown) + + document.documentElement.ontouchstart = () => {} + spyOn(EventHandler, 'off') + + btnDropdown.addEventListener('shown.bs.dropdown', () => { + dropdown.hide() + }) + + btnDropdown.addEventListener('hidden.bs.dropdown', () => { + expect(btnDropdown.classList.contains('show')).toEqual(false) + expect(btnDropdown.getAttribute('aria-expanded')).toEqual('false') + expect(EventHandler.off).toHaveBeenCalled() + + document.documentElement.ontouchstart = defaultValueOnTouchStart + done() + }) + + dropdown.show() + }) }) describe('dispose', () => {