Skip to content

Commit

Permalink
fix(tap): Prevent different input focus after 300ms delay
Browse files Browse the repository at this point in the history
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
  • Loading branch information
Adam Bradley committed May 8, 2014
1 parent 0325b87 commit 8730e62
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 1 deletion.
8 changes: 7 additions & 1 deletion js/utils/tap.js
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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.
Expand Down Expand 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();
}

Expand Down
41 changes: 41 additions & 0 deletions test/unit/utils/tap.unit.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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: {
Expand Down

0 comments on commit 8730e62

Please sign in to comment.