@@ -63,7 +63,7 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming,
6363 ctrl . select = select ;
6464 ctrl . listEnter = onListEnter ;
6565 ctrl . listLeave = onListLeave ;
66- ctrl . mouseUp = onMouseup ;
66+ ctrl . focusInput = focusInputElement ;
6767 ctrl . getCurrentDisplayValue = getCurrentDisplayValue ;
6868 ctrl . registerSelectedItemWatcher = registerSelectedItemWatcher ;
6969 ctrl . unregisterSelectedItemWatcher = unregisterSelectedItemWatcher ;
@@ -103,6 +103,10 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming,
103103 gatherElements ( ) ;
104104 moveDropdown ( ) ;
105105
106+ // Touch devices often do not send a click event on tap. We still want to focus the input
107+ // and open the options pop-up in these cases.
108+ $element . on ( 'touchstart' , focusInputElement ) ;
109+
106110 // Forward all focus events to the input element when autofocus is enabled
107111 if ( $scope . autofocus ) {
108112 $element . on ( 'focus' , focusInputElement ) ;
@@ -366,12 +370,31 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming,
366370
367371 // event/change handlers
368372
373+ /**
374+ * @param {Event } $event
375+ */
376+ function preventDefault ( $event ) {
377+ $event . preventDefault ( ) ;
378+ }
379+
380+ /**
381+ * @param {Event } $event
382+ */
383+ function stopPropagation ( $event ) {
384+ $event . stopPropagation ( ) ;
385+ }
386+
369387 /**
370388 * Handles changes to the `hidden` property.
371- * @param {boolean } hidden
372- * @param {boolean } oldHidden
389+ * @param {boolean } hidden true to hide the options pop-up, false to show it.
390+ * @param {boolean } oldHidden the previous value of hidden
373391 */
374392 function handleHiddenChange ( hidden , oldHidden ) {
393+ var scrollContainerElement ;
394+
395+ if ( elements ) {
396+ scrollContainerElement = angular . element ( elements . scrollContainer ) ;
397+ }
375398 if ( ! hidden && oldHidden ) {
376399 positionDropdown ( ) ;
377400
@@ -380,13 +403,23 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming,
380403 reportMessages ( true , ReportType . Count | ReportType . Selected ) ;
381404
382405 if ( elements ) {
383- $mdUtil . disableScrollAround ( elements . ul ) ;
384- enableWrapScroll = disableElementScrollEvents ( angular . element ( elements . wrap ) ) ;
385- ctrl . documentElement . on ( 'click' , handleClickOutside ) ;
406+ $mdUtil . disableScrollAround ( elements . scrollContainer ) ;
407+ enableWrapScroll = disableElementScrollEvents ( elements . wrap ) ;
408+ if ( $mdUtil . isIos ) {
409+ ctrl . documentElement . on ( 'touchend' , handleTouchOutsidePanel ) ;
410+ if ( scrollContainerElement ) {
411+ scrollContainerElement . on ( 'touchstart touchmove touchend' , stopPropagation ) ;
412+ }
413+ }
386414 $mdUtil . nextTick ( updateActiveOption ) ;
387415 }
388416 } else if ( hidden && ! oldHidden ) {
389- ctrl . documentElement . off ( 'click' , handleClickOutside ) ;
417+ if ( $mdUtil . isIos ) {
418+ ctrl . documentElement . off ( 'touchend' , handleTouchOutsidePanel ) ;
419+ if ( scrollContainerElement ) {
420+ scrollContainerElement . off ( 'touchstart touchmove touchend' , stopPropagation ) ;
421+ }
422+ }
390423 $mdUtil . enableScrolling ( ) ;
391424
392425 if ( enableWrapScroll ) {
@@ -397,29 +430,27 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming,
397430 }
398431
399432 /**
400- * Handling click events that bubble up to the document is required for closing the dropdown
401- * panel on click outside of the panel on iOS.
433+ * Handling touch events that bubble up to the document is required for closing the dropdown
434+ * panel on touch outside of the options pop-up panel on iOS.
402435 * @param {Event } $event
403436 */
404- function handleClickOutside ( $event ) {
437+ function handleTouchOutsidePanel ( $event ) {
405438 ctrl . hidden = true ;
439+ // iOS does not blur the pop-up for touches on the scroll mask, so we have to do it.
440+ doBlur ( true ) ;
406441 }
407442
408443 /**
409- * Disables scrolling for a specific element
444+ * Disables scrolling for a specific element.
445+ * @param {!string|!DOMElement } element to disable scrolling
446+ * @return {Function } function to call to re-enable scrolling for the element
410447 */
411448 function disableElementScrollEvents ( element ) {
412-
413- function preventDefault ( e ) {
414- e . preventDefault ( ) ;
415- }
416-
417- element . on ( 'wheel' , preventDefault ) ;
418- element . on ( 'touchmove' , preventDefault ) ;
449+ var elementToDisable = angular . element ( element ) ;
450+ elementToDisable . on ( 'wheel touchmove' , preventDefault ) ;
419451
420452 return function ( ) {
421- element . off ( 'wheel' , preventDefault ) ;
422- element . off ( 'touchmove' , preventDefault ) ;
453+ elementToDisable . off ( 'wheel touchmove' , preventDefault ) ;
423454 } ;
424455 }
425456
@@ -439,13 +470,6 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming,
439470 ctrl . hidden = shouldHide ( ) ;
440471 }
441472
442- /**
443- * When the mouse button is released, send focus back to the input field.
444- */
445- function onMouseup ( ) {
446- elements . input . focus ( ) ;
447- }
448-
449473 /**
450474 * Handles changes to the selected item.
451475 * @param selectedItem
@@ -673,7 +697,7 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming,
673697
674698 /**
675699 * Returns the display value for an item.
676- * @param item
700+ * @param { * } item
677701 * @returns {* }
678702 */
679703 function getDisplayValue ( item ) {
@@ -689,7 +713,7 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming,
689713 /**
690714 * Getter function to invoke user-defined expression (in the directive)
691715 * to convert your object to a single string.
692- * @param item
716+ * @param { * } item
693717 * @returns {string|null }
694718 */
695719 function getItemText ( item ) {
@@ -699,7 +723,7 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming,
699723
700724 /**
701725 * Returns the locals object for compiling item templates.
702- * @param item
726+ * @param { * } item
703727 * @returns {Object|undefined }
704728 */
705729 function getItemAsNameVal ( item ) {
@@ -837,14 +861,14 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming,
837861 * Defines a public property with a handler and a default value.
838862 * @param {string } key
839863 * @param {Function } handler function
840- * @param {* } value default value
864+ * @param {* } defaultValue default value
841865 */
842- function defineProperty ( key , handler , value ) {
866+ function defineProperty ( key , handler , defaultValue ) {
843867 Object . defineProperty ( ctrl , key , {
844- get : function ( ) { return value ; } ,
868+ get : function ( ) { return defaultValue ; } ,
845869 set : function ( newValue ) {
846- var oldValue = value ;
847- value = newValue ;
870+ var oldValue = defaultValue ;
871+ defaultValue = newValue ;
848872 handler ( newValue , oldValue ) ;
849873 }
850874 } ) ;
@@ -1014,7 +1038,7 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming,
10141038 function updateVirtualScroll ( ) {
10151039 // elements in virtual scroll have consistent heights
10161040 var optionHeight = elements . li [ 0 ] . offsetHeight ,
1017- top = optionHeight * ctrl . index ,
1041+ top = optionHeight * Math . max ( 0 , ctrl . index ) ,
10181042 bottom = top + optionHeight ,
10191043 containerHeight = elements . scroller . clientHeight ,
10201044 scrollTop = elements . scroller . scrollTop ;
@@ -1028,7 +1052,7 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming,
10281052
10291053 function updateStandardScroll ( ) {
10301054 // elements in standard scroll have variable heights
1031- var selected = elements . li [ ctrl . index ] || elements . li [ 0 ] ;
1055+ var selected = elements . li [ Math . max ( 0 , ctrl . index ) ] ;
10321056 var containerHeight = elements . scrollContainer . offsetHeight ,
10331057 top = selected && selected . offsetTop || 0 ,
10341058 bottom = top + selected . clientHeight ,
0 commit comments