Skip to content
This repository was archived by the owner on Sep 5, 2024. It is now read-only.

Commit f81349a

Browse files
Splaktarandrewseguin
authored andcommitted
fix(autocomplete): tap outside options panel on iOS does not close panel (#11625)
Fixes #9581.
1 parent ffe9349 commit f81349a

File tree

3 files changed

+50
-22
lines changed

3 files changed

+50
-22
lines changed

src/components/autocomplete/js/autocompleteController.js

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,15 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming,
2727
debouncedOnResize = $mdUtil.debounce(onWindowResize),
2828
mode = MODE_VIRTUAL; // default
2929

30+
/**
31+
* The root document element. This is used for attaching a top-level click handler to
32+
* close the options panel when a click outside said panel occurs. We use `documentElement`
33+
* instead of body because, when scrolling is disabled, some browsers consider the body element
34+
* to be completely off the screen and propagate events directly to the html element.
35+
* @type {!angular.JQLite}
36+
*/
37+
ctrl.documentElement = angular.element(document.documentElement);
38+
3039
// Public Exported Variables with handlers
3140
defineProperty('hidden', handleHiddenChange, true);
3241

@@ -356,8 +365,10 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming,
356365
if (elements) {
357366
$mdUtil.disableScrollAround(elements.ul);
358367
enableWrapScroll = disableElementScrollEvents(angular.element(elements.wrap));
368+
ctrl.documentElement.on('click', handleClickOutside);
359369
}
360370
} else if (hidden && !oldHidden) {
371+
ctrl.documentElement.off('click', handleClickOutside);
361372
$mdUtil.enableScrolling();
362373

363374
if (enableWrapScroll) {
@@ -367,6 +378,15 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming,
367378
}
368379
}
369380

381+
/**
382+
* Handling click events that bubble up to the document is required for closing the dropdown
383+
* panel on click outside of the panel on iOS.
384+
* @param {Event} $event
385+
*/
386+
function handleClickOutside($event) {
387+
ctrl.hidden = true;
388+
}
389+
370390
/**
371391
* Disables scrolling for a specific element
372392
*/
@@ -539,7 +559,7 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming,
539559

540560
/**
541561
* Force blur on input element
542-
* @param forceBlur
562+
* @param {boolean} forceBlur
543563
*/
544564
function doBlur(forceBlur) {
545565
if (forceBlur) {
@@ -833,8 +853,12 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming,
833853

834854
/**
835855
* Clears the searchText value and selected item.
856+
* @param {Event} $event
836857
*/
837-
function clearValue () {
858+
function clearValue ($event) {
859+
if ($event) {
860+
$event.stopPropagation();
861+
}
838862
clearSelectedItem();
839863
clearSearchText();
840864
}

src/components/bottomSheet/bottom-sheet.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -244,14 +244,14 @@ function MdBottomSheetProvider($$interimElementProvider) {
244244
// Add a backdrop that will close on click
245245
backdrop = $mdUtil.createBackdrop(scope, "md-bottom-sheet-backdrop md-opaque");
246246

247-
// Prevent mouse focus on backdrop; ONLY programatic focus allowed.
248-
// This allows clicks on backdrop to propogate to the $rootElement and
247+
// Prevent mouse focus on backdrop; ONLY programmatic focus allowed.
248+
// This allows clicks on backdrop to propagate to the $rootElement and
249249
// ESC key events to be detected properly.
250250
backdrop[0].tabIndex = -1;
251251

252252
if (options.clickOutsideToClose) {
253253
backdrop.on('click', function() {
254-
$mdUtil.nextTick($mdBottomSheet.cancel,true);
254+
$mdUtil.nextTick($mdBottomSheet.cancel, true);
255255
});
256256
}
257257

@@ -277,7 +277,7 @@ function MdBottomSheetProvider($$interimElementProvider) {
277277
if (options.escapeToClose) {
278278
options.rootElementKeyupCallback = function(e) {
279279
if (e.keyCode === $mdConstant.KEY_CODE.ESCAPE) {
280-
$mdUtil.nextTick($mdBottomSheet.cancel,true);
280+
$mdUtil.nextTick($mdBottomSheet.cancel, true);
281281
}
282282
};
283283

@@ -337,7 +337,7 @@ function MdBottomSheetProvider($$interimElementProvider) {
337337
var distanceRemaining = element.prop('offsetHeight') - ev.pointer.distanceY;
338338
var transitionDuration = Math.min(distanceRemaining / ev.pointer.velocityY * 0.75, 500);
339339
element.css($mdConstant.CSS.TRANSITION_DURATION, transitionDuration + 'ms');
340-
$mdUtil.nextTick($mdBottomSheet.cancel,true);
340+
$mdUtil.nextTick($mdBottomSheet.cancel, true);
341341
} else {
342342
element.css($mdConstant.CSS.TRANSITION_DURATION, '');
343343
element.css($mdConstant.CSS.TRANSFORM, '');

src/core/util/util.js

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -215,10 +215,10 @@ function UtilFactory($document, $timeout, $compile, $rootScope, $$mdAnimate, $in
215215

216216
/**
217217
* Disables scroll around the passed parent element.
218-
* @param element Unused
218+
* @param {!Element|!angular.JQLite} element Origin Element (not used)
219219
* @param {!Element|!angular.JQLite} parent Element to disable scrolling within.
220220
* Defaults to body if none supplied.
221-
* @param options Object of options to modify functionality
221+
* @param {Object=} options Object of options to modify functionality
222222
* - disableScrollMask Boolean of whether or not to create a scroll mask element or
223223
* use the passed parent element.
224224
*/
@@ -234,7 +234,7 @@ function UtilFactory($document, $timeout, $compile, $rootScope, $$mdAnimate, $in
234234

235235
var body = $document[0].body;
236236
var restoreBody = disableBodyScroll();
237-
var restoreElement = disableElementScroll(parent);
237+
var restoreElement = disableElementScroll(parent, options);
238238

239239
return $mdUtil.disableScrollAround._restoreScroll = function() {
240240
if (--$mdUtil.disableScrollAround._count <= 0) {
@@ -247,21 +247,29 @@ function UtilFactory($document, $timeout, $compile, $rootScope, $$mdAnimate, $in
247247

248248
/**
249249
* Creates a virtual scrolling mask to prevent touchmove, keyboard, scrollbar clicking,
250-
* and wheel events
250+
* and wheel events.
251+
* @param {!Element|!angular.JQLite} elementToDisable
252+
* @param {Object=} scrollMaskOptions Object of options to modify functionality
253+
* - disableScrollMask Boolean of whether or not to create a scroll mask element or
254+
* use the passed parent element.
255+
* @returns {Function}
251256
*/
252-
function disableElementScroll(element) {
253-
element = angular.element(element || body);
254-
257+
function disableElementScroll(elementToDisable, scrollMaskOptions) {
255258
var scrollMask;
259+
var wrappedElementToDisable = angular.element(elementToDisable || body);
256260

257-
if (options.disableScrollMask) {
258-
scrollMask = element;
261+
if (scrollMaskOptions.disableScrollMask) {
262+
scrollMask = wrappedElementToDisable;
259263
} else {
260264
scrollMask = angular.element(
261265
'<div class="md-scroll-mask">' +
262266
' <div class="md-scroll-mask-bar"></div>' +
263267
'</div>');
264-
element.append(scrollMask);
268+
wrappedElementToDisable.append(scrollMask);
269+
}
270+
271+
function preventDefault(e) {
272+
e.preventDefault();
265273
}
266274

267275
scrollMask.on('wheel', preventDefault);
@@ -271,14 +279,10 @@ function UtilFactory($document, $timeout, $compile, $rootScope, $$mdAnimate, $in
271279
scrollMask.off('wheel');
272280
scrollMask.off('touchmove');
273281

274-
if (!options.disableScrollMask && scrollMask[0].parentNode) {
282+
if (!scrollMaskOptions.disableScrollMask && scrollMask[0].parentNode) {
275283
scrollMask[0].parentNode.removeChild(scrollMask[0]);
276284
}
277285
};
278-
279-
function preventDefault(e) {
280-
e.preventDefault();
281-
}
282286
}
283287

284288
// Converts the body to a position fixed block and translate it to the proper scroll position

0 commit comments

Comments
 (0)