diff --git a/docs/app/js/app.js b/docs/app/js/app.js
index 6d027fbda35..b633f095187 100644
--- a/docs/app/js/app.js
+++ b/docs/app/js/app.js
@@ -144,8 +144,9 @@ function(SERVICES, COMPONENTS, DEMOS, PAGES,
}])
-.config(['AngularyticsProvider', function(AngularyticsProvider) {
- AngularyticsProvider.setEventHandlers(['GoogleUniversal']);
+.config(['$mdGestureProvider', 'AngularyticsProvider', function($mdGestureProvider, AngularyticsProvider) {
+ $mdGestureProvider.skipClickHijack();
+ AngularyticsProvider.setEventHandlers(['GoogleUniversal']);
}])
.run(['$rootScope', '$window', 'Angularytics', function($rootScope, $window, Angularytics) {
diff --git a/src/components/autocomplete/autocomplete.scss b/src/components/autocomplete/autocomplete.scss
index ddc91274497..477785a09ca 100644
--- a/src/components/autocomplete/autocomplete.scss
+++ b/src/components/autocomplete/autocomplete.scss
@@ -28,7 +28,6 @@ md-autocomplete {
}
.md-show-clear-button {
-
button {
display: block;
position: absolute;
@@ -43,10 +42,8 @@ md-autocomplete {
@include rtl-prop(padding-right, padding-left, $md-autocomplete-clear-size, 0);
}
}
-
}
md-autocomplete-wrap {
-
// Layout [layout='row']
display: flex;
flex-direction: row;
@@ -59,9 +56,10 @@ md-autocomplete {
z-index: $z-index-backdrop + 1;
}
- md-input-container, input {
+ md-input-container,
+ input {
// Layout [flex]
- flex: 1 1 0%;
+ flex: 1 1 0;
box-sizing: border-box;
min-width : 0;
}
diff --git a/src/components/autocomplete/js/autocompleteController.js b/src/components/autocomplete/js/autocompleteController.js
index 8dbfdb7e7ea..6279d155728 100644
--- a/src/components/autocomplete/js/autocompleteController.js
+++ b/src/components/autocomplete/js/autocompleteController.js
@@ -63,7 +63,7 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming,
ctrl.select = select;
ctrl.listEnter = onListEnter;
ctrl.listLeave = onListLeave;
- ctrl.mouseUp = onMouseup;
+ ctrl.focusInput = focusInputElement;
ctrl.getCurrentDisplayValue = getCurrentDisplayValue;
ctrl.registerSelectedItemWatcher = registerSelectedItemWatcher;
ctrl.unregisterSelectedItemWatcher = unregisterSelectedItemWatcher;
@@ -103,6 +103,10 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming,
gatherElements();
moveDropdown();
+ // Touch devices often do not send a click event on tap. We still want to focus the input
+ // and open the options pop-up in these cases.
+ $element.on('touchstart', focusInputElement);
+
// Forward all focus events to the input element when autofocus is enabled
if ($scope.autofocus) {
$element.on('focus', focusInputElement);
@@ -366,12 +370,31 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming,
// event/change handlers
+ /**
+ * @param {Event} $event
+ */
+ function preventDefault($event) {
+ $event.preventDefault();
+ }
+
+ /**
+ * @param {Event} $event
+ */
+ function stopPropagation($event) {
+ $event.stopPropagation();
+ }
+
/**
* Handles changes to the `hidden` property.
- * @param {boolean} hidden
- * @param {boolean} oldHidden
+ * @param {boolean} hidden true to hide the options pop-up, false to show it.
+ * @param {boolean} oldHidden the previous value of hidden
*/
function handleHiddenChange (hidden, oldHidden) {
+ var scrollContainerElement;
+
+ if (elements) {
+ scrollContainerElement = angular.element(elements.scrollContainer);
+ }
if (!hidden && oldHidden) {
positionDropdown();
@@ -380,13 +403,23 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming,
reportMessages(true, ReportType.Count | ReportType.Selected);
if (elements) {
- $mdUtil.disableScrollAround(elements.ul);
- enableWrapScroll = disableElementScrollEvents(angular.element(elements.wrap));
- ctrl.documentElement.on('click', handleClickOutside);
+ $mdUtil.disableScrollAround(elements.scrollContainer);
+ enableWrapScroll = disableElementScrollEvents(elements.wrap);
+ if ($mdUtil.isIos) {
+ ctrl.documentElement.on('touchend', handleTouchOutsidePanel);
+ if (scrollContainerElement) {
+ scrollContainerElement.on('touchstart touchmove touchend', stopPropagation);
+ }
+ }
$mdUtil.nextTick(updateActiveOption);
}
} else if (hidden && !oldHidden) {
- ctrl.documentElement.off('click', handleClickOutside);
+ if ($mdUtil.isIos) {
+ ctrl.documentElement.off('touchend', handleTouchOutsidePanel);
+ if (scrollContainerElement) {
+ scrollContainerElement.off('touchstart touchmove touchend', stopPropagation);
+ }
+ }
$mdUtil.enableScrolling();
if (enableWrapScroll) {
@@ -397,29 +430,27 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming,
}
/**
- * Handling click events that bubble up to the document is required for closing the dropdown
- * panel on click outside of the panel on iOS.
+ * Handling touch events that bubble up to the document is required for closing the dropdown
+ * panel on touch outside of the options pop-up panel on iOS.
* @param {Event} $event
*/
- function handleClickOutside($event) {
+ function handleTouchOutsidePanel($event) {
ctrl.hidden = true;
+ // iOS does not blur the pop-up for touches on the scroll mask, so we have to do it.
+ doBlur(true);
}
/**
- * Disables scrolling for a specific element
+ * Disables scrolling for a specific element.
+ * @param {!string|!DOMElement} element to disable scrolling
+ * @return {Function} function to call to re-enable scrolling for the element
*/
function disableElementScrollEvents(element) {
-
- function preventDefault(e) {
- e.preventDefault();
- }
-
- element.on('wheel', preventDefault);
- element.on('touchmove', preventDefault);
+ var elementToDisable = angular.element(element);
+ elementToDisable.on('wheel touchmove', preventDefault);
return function() {
- element.off('wheel', preventDefault);
- element.off('touchmove', preventDefault);
+ elementToDisable.off('wheel touchmove', preventDefault);
};
}
@@ -439,13 +470,6 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming,
ctrl.hidden = shouldHide();
}
- /**
- * When the mouse button is released, send focus back to the input field.
- */
- function onMouseup () {
- elements.input.focus();
- }
-
/**
* Handles changes to the selected item.
* @param selectedItem
@@ -673,7 +697,7 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming,
/**
* Returns the display value for an item.
- * @param item
+ * @param {*} item
* @returns {*}
*/
function getDisplayValue (item) {
@@ -689,7 +713,7 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming,
/**
* Getter function to invoke user-defined expression (in the directive)
* to convert your object to a single string.
- * @param item
+ * @param {*} item
* @returns {string|null}
*/
function getItemText (item) {
@@ -699,7 +723,7 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming,
/**
* Returns the locals object for compiling item templates.
- * @param item
+ * @param {*} item
* @returns {Object|undefined}
*/
function getItemAsNameVal (item) {
@@ -837,14 +861,14 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming,
* Defines a public property with a handler and a default value.
* @param {string} key
* @param {Function} handler function
- * @param {*} value default value
+ * @param {*} defaultValue default value
*/
- function defineProperty (key, handler, value) {
+ function defineProperty (key, handler, defaultValue) {
Object.defineProperty(ctrl, key, {
- get: function () { return value; },
+ get: function () { return defaultValue; },
set: function (newValue) {
- var oldValue = value;
- value = newValue;
+ var oldValue = defaultValue;
+ defaultValue = newValue;
handler(newValue, oldValue);
}
});
@@ -1014,7 +1038,7 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming,
function updateVirtualScroll() {
// elements in virtual scroll have consistent heights
var optionHeight = elements.li[0].offsetHeight,
- top = optionHeight * ctrl.index,
+ top = optionHeight * Math.max(0, ctrl.index),
bottom = top + optionHeight,
containerHeight = elements.scroller.clientHeight,
scrollTop = elements.scroller.scrollTop;
@@ -1028,7 +1052,7 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming,
function updateStandardScroll() {
// elements in standard scroll have variable heights
- var selected = elements.li[ctrl.index] || elements.li[0];
+ var selected = elements.li[Math.max(0, ctrl.index)];
var containerHeight = elements.scrollContainer.offsetHeight,
top = selected && selected.offsetTop || 0,
bottom = top + selected.clientHeight,
diff --git a/src/components/autocomplete/js/autocompleteDirective.js b/src/components/autocomplete/js/autocompleteDirective.js
index 2d94f9be83a..30cbaa08bc6 100644
--- a/src/components/autocomplete/js/autocompleteDirective.js
+++ b/src/components/autocomplete/js/autocompleteDirective.js
@@ -98,7 +98,7 @@ angular
* @param {number=} md-delay Specifies the amount of time (in milliseconds) to wait before looking
* for results.
* @param {boolean=} md-clear-button Whether the clear button for the autocomplete input should show
- * up or not.
+ * up or not. When `md-floating-label` is set, defaults to false, defaults to true otherwise.
* @param {boolean=} md-autofocus If true, the autocomplete will be automatically focused when a
* `$mdDialog`, `$mdBottomsheet` or `$mdSidenav`, which contains the autocomplete, is opening.
*
@@ -365,7 +365,7 @@ function MdAutocomplete ($$mdSvgRegistry) {
// Stop click events from bubbling up to the document and triggering a flicker of the
// options panel while still supporting ng-click to be placed on md-autocomplete.
- element.on('click', function(event) {
+ element.on('click touchstart touchend', function(event) {
event.stopPropagation();
});
};
@@ -402,7 +402,7 @@ function MdAutocomplete ($$mdSvgRegistry) {
id="ul-{{$mdAutocompleteCtrl.id}}"\
ng-mouseenter="$mdAutocompleteCtrl.listEnter()"\
ng-mouseleave="$mdAutocompleteCtrl.listLeave()"\
- ng-mouseup="$mdAutocompleteCtrl.mouseUp()"\
+ ng-mouseup="$mdAutocompleteCtrl.focusInput()"\
role="listbox">\