1
0
mirror of https://github.com/twbs/bootstrap.git synced 2025-08-13 17:14:04 +02:00

Replace dropdown backdrop hack with cleaner JS-only hack

* Replace backdrop with simple noop mouse listener

As discussed in https://github.com/twbs/bootstrap/pull/22422 the current
approach of injecting a backdrop (to work around iOS' broken event
delegation for the `click` event) has annoying consequences on
touch-enabled laptop/desktop devices.
Instead of a backdrop `<div>`, here we simply add extra empty/noop
mouse listeners to the immediate children of `<body>` (and remove
them when the dropdown is closed) in order to force iOS to properly
bubble a `click` resulting from a tap (essentially, method 2 from
https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html)
This is sufficient (except in rare cases where the user does manage to tap
on the body itself, rather than any child elements of body - which is not
very likely in an iOS phone/tablet scenario for most layouts) to get iOS to
get a grip and do the correct event bubbling/delegation, meaning the regular
"click" event will bubble back to the `<body>` when tapping outside of the dropdown,
and the dropdown will close properly (just like it already does, even without
this fix, in non-iOS touchscreen devices/browsers, like Chrome/Android and
Windows on a touch laptop).
This approach, though a bit hacky, has no impact on the DOM structure, and
has no unforeseen side effects on touch-enabled laptops/desktops. And crucially,
it works just fine in iOS.

* Remove dropdown backdrop styles

* Update doc for dropdowns and touch-enabled devices
This commit is contained in:
Patrick H. Lauke
2017-04-14 09:19:00 +01:00
committed by GitHub
parent 3b3366e1b6
commit 6d64afe508
4 changed files with 12 additions and 26 deletions

View File

@@ -43,13 +43,11 @@ const Dropdown = (($) => {
}
const ClassName = {
BACKDROP : 'dropdown-backdrop',
DISABLED : 'disabled',
SHOW : 'show'
}
const Selector = {
BACKDROP : '.dropdown-backdrop',
DATA_TOGGLE : '[data-toggle="dropdown"]',
FORM_CHILD : '.dropdown form',
MENU : '.dropdown-menu',
@@ -107,16 +105,13 @@ const Dropdown = (($) => {
return false
}
// set the backdrop only if the dropdown menu will be opened
// if this is a touch-enabled device we add extra
// empty mouseover listeners to the body's immediate children;
// only needed because of broken event delegation on iOS
// https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html
if ('ontouchstart' in document.documentElement &&
!$(parent).closest(Selector.NAVBAR_NAV).length) {
// if touch-enabled device we use a backdrop because click events
// don't delegate on iOS - see https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html
const backdrop = document.createElement('div')
backdrop.className = ClassName.BACKDROP
$(backdrop).insertBefore(this)
$(backdrop).on('click', Dropdown._clearMenus)
$('body').children().on('mouseover', '*', $.noop)
}
this.focus()
@@ -192,10 +187,10 @@ const Dropdown = (($) => {
continue
}
// remove backdrop only if the dropdown menu will be hidden
const backdrop = $(parent).find(Selector.BACKDROP)[0]
if (backdrop) {
backdrop.parentNode.removeChild(backdrop)
// if this is a touch-enabled device we remove the extra
// empty mouseover listeners we added for iOS support
if ('ontouchstart' in document.documentElement) {
$('body').children().off('mouseover', '*', $.noop)
}
toggles[i].setAttribute('aria-expanded', 'false')