fix(tap): Prevent different input focus after 300ms delay

If a text input is located in the same area as a button which was just
tapped, which was probably because of a view transition, the text input
gets focus 300ms later. This is an issue on Android because it also
fires off a mousedown event. Resolved by remembering the touchend
target then checking if it’s different from the mousedown target.
Closes #1370
This commit is contained in:
Adam Bradley
2014-05-08 10:40:10 -05:00
parent 0325b87d49
commit 8730e62e62
2 changed files with 48 additions and 1 deletions

View File

@@ -66,6 +66,7 @@ var tapMouseResetTimer;
var tapPointerMoved;
var tapPointerStart;
var tapTouchFocusedInput;
var tapLastTouchTarget;
var TAP_RELEASE_TOLERANCE = 6; // how much the coordinates can be off between start/end, but still a click
@@ -259,7 +260,7 @@ function tapMouseDown(e) {
console.log('mousedown', 'stop event');
e.stopPropagation();
if( !ionic.tap.isTextInput(e.target) ) {
if( !ionic.tap.isTextInput(e.target) || tapLastTouchTarget !== e.target ) {
// If you preventDefault on a text input then you cannot move its text caret/cursor.
// Allow through only the text input default. However, without preventDefault on an
// input the 300ms delay can change focus on inputs after the keyboard shows up.
@@ -336,8 +337,13 @@ function tapTouchEnd(e) {
tapEnableTouchEvents();
if( !tapHasPointerMoved(e) ) {
tapClick(e);
if( e.target.tagName === 'SELECT' ) {
e.preventDefault();
}
}
tapLastTouchTarget = e.target;
tapTouchCancel();
}

View File

@@ -405,6 +405,26 @@ describe('Ionic Tap', function() {
expect( e.dispatchedEvent ).toBeUndefined();
});
it('Should preventDefault on touchend when the target is a select', function() {
var e = {
target: document.createElement('select'),
clientX: 100, clientY: 100,
dispatchEvent: function(){ this.dispatchedEvent = true; },
preventDefault:function(){ this.preventedDefault = true; }
};
tapTouchEnd(e);
expect( e.preventedDefault ).toEqual(true);
e = {
target: document.createElement('div'),
clientX: 100, clientY: 100,
dispatchEvent: function(){ this.dispatchedEvent = true; },
preventDefault:function(){ this.preventedDefault = true; }
};
tapTouchEnd(e);
expect( e.preventedDefault ).toBeUndefined();
});
it('Should cancel click when mousemove coordinates goes too far from mousedown coordinates', function() {
var e = { clientX: 100, clientY: 100 };
tapMouseDown(e);
@@ -442,6 +462,27 @@ describe('Ionic Tap', function() {
expect( e.isTapHandled ).toEqual(false);
});
it('Should preventDefault on mousedown if touchend target is different than mousedown target', function() {
tapLastTouchTarget = null;
var touchEndEvent = {
target: document.createElement('button'),
clientX: 100, clientY: 100,
preventDefault: function(){ this.defaultedPrevented = true; }
};
tapTouchEnd(touchEndEvent);
expect( tapLastTouchTarget ).toEqual(touchEndEvent.target);
var mouseDownEvent = {
target: document.createElement('textarea'),
clientX: 100, clientY: 100,
preventDefault: function(){ this.defaultedPrevented = true; },
stopPropagation: function(){ this.stoppedPropagation = true; }
};
tapMouseDown(mouseDownEvent);
expect( mouseDownEvent.defaultedPrevented ).toEqual(true);
});
it('Should tapClick with touchend and fire immediately', function() {
var e = {
target: {