Material: autocomplete: suggestion box not closing on blur

Created on 12 Sep 2016  路  16Comments  路  Source: angular/material

Reopening #7419 as suggested by @EladBezalel

Actual Behavior:
Inputs are not blurred when tapping outside of a focussed input element.

The autocomplete suggestion box is not closing when the user taps outside the autocomplete to close the suggestion box without selecting a value.

This only happens on touch devices. Tested on Android/Chrome 52.0.2743.98 and iPhone/Safari 9.

When using the "Done" on iOS keyboard the suggestion box closes.
Hiding the keyboard on Android did not close the suggestion box.

Expected Behavior:
The autocomplete suggestion box should close when the user blurs the control by clicking/touching outside of it.

Additional Info

According to the comments in #7419 it worked in 0.11.4.

The issue might origin from function mouseInputHijacker and a quick-and-dirty fix for the autocomplete may be applying a css rule:

.md-scroll-mask {
  display: none;
}
- Lots of Comments required sync Pull Request iOS fixed inconvenient bug gestures mobile

Most helpful comment

_Quote from the source_

If hijack clicks is true, we preventDefault any click that wasn't sent by ngMaterial. This is because on older Android & iOS, a false, or 'ghost' click event will be sent ~400ms after a touchend event happens.

The only way to know if this click is real is to prevent any normal click events, and add a flag to events sent by material so we know not to prevent those.

Two exceptions to click events that should be prevented are:

  • click events sent by the keyboard (eg form submit)
  • events that originate from an Ionic app

Basically the Fastclick solution wouldn't be available for older devices anymore.

A very good article about the ghost clicks (and Fastclick)

All 16 comments

Related to the planned gesture improvements - #9362

In meanwhile, disabling the click hijack fixes that issue

$mdGestureProvider.skipClickHijack();

@DevVersion Has $mdGestureProvider.skipClickHijack(); any negative side-effects?

_Quote from the source_

If hijack clicks is true, we preventDefault any click that wasn't sent by ngMaterial. This is because on older Android & iOS, a false, or 'ghost' click event will be sent ~400ms after a touchend event happens.

The only way to know if this click is real is to prevent any normal click events, and add a flag to events sent by material so we know not to prevent those.

Two exceptions to click events that should be prevented are:

  • click events sent by the keyboard (eg form submit)
  • events that originate from an Ionic app

Basically the Fastclick solution wouldn't be available for older devices anymore.

A very good article about the ghost clicks (and Fastclick)

Possible dupe of #8996

@clshortfuse Yeah those are kind of the same, but I personally prefer this one, because it includes more information e.g a workaround and why it's happening (also is about the input)

We can close on of both :)

@DevVersion Yep, I'm tagging gestures to things that will have to be revisited after/with HammerJS.

@devversion I think we can more accurately target devices so click hijacking isn't done on newer devices. It's mix of reading the user agent, browser version and viewport scaling. I have to write it anyway for an app I'm writing. Of this top of my head, Safari Mobile pre-July 2016 need it and Android pre-2013/2014. I can get the exact version numbers later.

@clshortfuse There is no need for Click hijacking with HammerJS anymore. We should just wait for this and not make any big changes to $mdGesture's yet.

Closing this issue into #9362, since it will be solved automatically when HammerJS is used.

I've found a hack that helps me get around this issue on the md-autocomplete suggestion box. This code is shortened to highlight the basics of how I did it:

1) always use a function to return the items in an array

<md-autocomplete
      md-no-cache="true"
      md-search-text="searchText"
      md-items="i in querySearch(searchText)">

2) When querySearch gets called in the controller, add an event listener for a click event on .md-scroll-mask, which triggers a blur()

var unFocus = function(){
    document.getElementsByTagName('md-autocomplete-wrap')[0].children[0].blur();
  };

$scope.querySearch = function(q){
    $timeout(function(){
      var mask = document.getElementsByClassName('md-scroll-mask')[0];
      if(mask) mask.addEventListener('click', unFocus, true);
    },250)
   //
   // Return a list of results ....
}

As an update to this issue, HammerJS is mentioned as a solution a few times above. We're tracking that work in https://github.com/angular/material/issues/9362, but it may not be something that we can ever get merged in.

Disabling click hijacking as described in https://github.com/angular/material/issues/9581#issuecomment-246305494 and our $mdGestureProvider docs is the recommended solution for now and likely going forward.

It's possible that we may just want to disable click hijacking by default (and allow it to be enabled) in 1.2.0.

There was no CodePen demo, so I created this one. In this case I called $mdGestureProvider.skipClickHijack(); but it did not help 馃槥 This is on an iPad with Chrome and iOS 12.1.3.

I tried the CSS from the OP to hide the md-scroll-mask but that doesn't appear to help either.

For some reason, on iOS, clicking on the md-scroll-mask causes the autocomplete's input to not get a blur event. On every other platform, a blur event is triggered and handled to provide the proper behavior.

I've looked into adding a click handler on the md-scroll-mask element which will fire a blur event on the element but so far that has been too eager. It often results in the dropdown appearing and then instantly disappearing. I'm investigating further...

OK, so this is caused by iOS Safari not firing blur and click events as you would expect on a desktop browser unless the touched element is a form element. This is due to their custom touch event implementation. In our case, the touched element most usually be a <div class="md-scroll-mask"> which doesn't qualify as a form element and thus Safari will generate no blur or click events.

Angular Material solves this by focusing the input when there is a click outside of the overlay.

AngularJS Material does not have any current way to get access to the input. We may be able to add another argument to $mdUtil.disableScrollAround() to allow passing in the associated form element that should get focus when the md-scroll-mask gets a touchend event.

I'm not able to reproduce this issue on recent versions of Chrome for Android.

I've got a PR ready to go that should fix this issue on iOS without any major API changes. It appears that listening on the document.documentElement for click events from the MdAutocompleteCtrl works.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

LeoLozes picture LeoLozes  路  3Comments

WebTechnolog picture WebTechnolog  路  3Comments

robertmesserle picture robertmesserle  路  3Comments

rdantonio picture rdantonio  路  3Comments

ddimitrop picture ddimitrop  路  3Comments