angular / angular.js

AngularJS - HTML enhanced for web apps!
https://angularjs.org
MIT License
58.85k stars 27.52k forks source link

ngTouch: iOs form elements loose focus when tapped to enter text (textareas,inputs) #6432

Closed doner-astogdill closed 8 years ago

doner-astogdill commented 10 years ago

While developing using ngTouch in an angular project I found that when tapping on a form field to enter text the keyboard would open and immediately close. I looked into ngTouch and found that line 308:

    // Blur focused form elements
    event.target && event.target.blur();

Was causing this. Once removed everything worked fine with no issues in my project. May want to look into a work around that would still allow function with iPad text entry.

ashleygwilliams commented 10 years ago

hi @doner-astogdill! could you provide a plnkr or a fiddle that demonstrates this behavior? thanks so much!

jdhiro commented 10 years ago

I can repro this and put a plunk together: http://plnkr.co/edit/gyNF9BhiaBuMtonPXYKS

At the moment I can only repro it in a UI Bootstrap modal, and ONLY on touch input devices (including touch emulation in Chrome). I haven't dug any deeper on it yet.

jdhiro commented 10 years ago

I tried commenting out the line event.target && event.target.blur(); and nothing was fixed, so I'm skeptical this is even the same issue.

jdhiro commented 10 years ago

Correction, removing both of the following lines resolves the issue:

I'll try to dig further tomorrow into why these are blurring forms in some places but not others.

jdhiro commented 10 years ago

OK, it appears that what is going on in my case (and probably the case of the OP) is that the form is wrapped in another element with an ng-click. touchend is firing as part of the parent element, which calls into preventGhostClick and sets a new lastPreventedTime. This confuses the onClick function on line 280.

This is evident by watching this function on line 281:

    if (Date.now() - lastPreventedTime > PREVENT_DURATION) {
      return; // Too old.
    }

On a normal form click, the lastPreventedTime is old, so the if statement is true and goes to return right away. However, if you have a form wrapped in a parent with ng-click, you end up with a lastPreventedTime of like 2 milliseconds ago and you start walking into code that calls blur().

This does actually seem like a bug in angular-touch to me. I don't think lastPreventedTime should be getting set/checked back to back on the same touch like this. That said, I'm not sure what the proper fix should be. I'll likely work around this by blocking touchend events in my own project.

soncodi commented 10 years ago

Ran across this issue with an input[type="text"] inside of a form within a bootstrap3 modal-body. Commenting line 320 event.target && event.target.blur() seems to fix it at first glance, but I haven't looked into it thoroughly.

Can repro on Cordova app on a Nexus5 and touch emulation mode in Chrome on Linux.

project707 commented 10 years ago

I just ran into this exact same issue, also within an Angular UI Bootstrap modal. It appears that the two libraries are currently incompatible in this respect as it doesn't allow normal focus even if the only element in the modal is the input.

Interestingly, it seems that you can hold a click and it will eventually focus.

@jdhiro your hack in issue 2017 worked nicely for the interim

eastbayjake commented 10 years ago

I'm also having this same issue with a text input inside an Angular UI Bootstrap modal, and like @project707 I noticed you can get the keyboard to pop up if you do a long tap.

Thanks to everyone for your answers!

eastbayjake commented 10 years ago

Just a follow-up: I implemented @jdhiro 's workaround and noticed that dismiss functionality in other modals stopped working. I haven't had time to look into why it's happening, but wanted to let other followers for this issue know.

pedubreuil commented 10 years ago

Any update about this issue? Did you find a workaround?

emosenkis commented 10 years ago

I'm also experiencing this issue with Chrome on Android. See also angular-ui/bootstrap#2017.

pedubreuil commented 10 years ago

Perfect, it works. Thank you :)

jbielick commented 10 years ago

The issue mainly boils down to having an <input> or other focusable element inside of an element that has an ng-click directive. Nesting the focusable element inside an element with an ngClick directive casuses this behavior.

The ui-bootstrap modal has an ngClick directive on the content window itself. I couldn't tell you why this is necessary. In the angular-foundation package, which shares almost exactly the same html for the modal template, the ng-click="close($event)" doesn't map to anything at all. It's scope holds no close property or function.

Essentially what's happening here is: 1.) you tap on an input inside of a modal (basically a div with an ng-click directive) 2.) the parent modal window's ngClick directive detects a touchstart event 3.) ngTouch records the coordinates at which the touchstart occurred so that it can clickbust the the resulting click event that will fire afterwards naturally. 4.) touchend fires and ngTouch reconciles the ending point of your touch with where it began and determines that if a click event fires in the same spot, it'll bust it. 5.) a click event fires on the x and y coordinates where you touched (the input) and ngTouch clickbusts this event since it isn't "allowed". Clickbusting involves e.preventDefault(), e.stopPropagation() and event.target.blur() as seen on https://github.com/angular/angular.js/blob/master/src/ngTouch/directive/ngClick.js#L161

Some have found that commenting line 161 will resolve this, but probably precludes other desired functionality that line was intended for. Obviously, modifying an angular package isn't ideal. In my case, we ended up removing the module altogether. This probably can also be alleviated by removing the ng-click directive from the modal window itself. Can anyone comment on why the modal window has an ng-click directive?

See this plunker for a distilled case of this issue. Make sure you "emulate touch screen" from the chrome (or other) dev tools.