fix(tap): Do not preventDefault after input focus, #1068

This commit is contained in:
Adam Bradley
2014-04-07 09:43:16 -05:00
parent a19e3b62f8
commit a977332f2b
2 changed files with 79 additions and 49 deletions

View File

@@ -23,34 +23,6 @@ describe('Ionic Tap', function() {
expect(targetEle.isFocused).toEqual(true);
});
it('Should preventDefault on an input', function() {
var targetEle = {
dispatchEvent: function() {},
focus: function() {}
};
ionic.tap.setTouchStart({ clientX: 100, clientY: 100 });
var e = {
clientX: 100, clientY: 100,
preventDefault: function() { this.preventedDefault = true }
};
targetEle.tagName = 'INPUT';
ionic.tap.simulateClick(targetEle, e);
expect(e.preventedDefault).toEqual(true);
e.preventedDefault = false;
targetEle.tagName = 'TEXTAREA';
ionic.tap.simulateClick(targetEle, e);
expect(e.preventedDefault).toEqual(true);
e.preventedDefault = false;
targetEle.tagName = 'DIV';
ionic.tap.simulateClick(targetEle, e);
expect(e.preventedDefault).toEqual(false);
e.preventedDefault = false;
});
it('Should setTouchStart and hasTouchScrolled true if >= touch tolerance', function() {
ionic.tap.setTouchStart({ clientX: 100, clientY: 100 });
@@ -183,13 +155,6 @@ describe('Ionic Tap', function() {
expect( ionic.tap.ignoreSimulateClick(targetEle) ).toEqual(true);
});
it('Should ignoreSimulateClick for input[file] elements', function() {
// Reported that on Android input[file] does not open using the tap
var targetEle = document.createElement('input');
targetEle.type = 'file';
expect( ionic.tap.ignoreSimulateClick(targetEle) ).toEqual(true);
});
it('Should ignoreSimulateClick for input[range] elements', function() {
// Range and tap do not agree, probably because it doesn't have a delay to begin with
var targetEle = document.createElement('input');
@@ -227,4 +192,56 @@ describe('Ionic Tap', function() {
expect(ionic.tap.isTapElement('P')).toEqual(false);
});
it('Should focus input', function() {
var ele = {
tagName: 'input',
focus: function(){ this.hasFocus=true; }
}
ionic.tap.handleFocus(ele);
expect( ele.hasFocus ).toEqual(true);
});
it('Should focus textarea', function() {
var ele = {
tagName: 'textarea',
focus: function(){ this.hasFocus=true; }
}
ionic.tap.handleFocus(ele);
expect( ele.hasFocus ).toEqual(true);
});
it('Should focus select', function() {
var ele = {
tagName: 'select',
focus: function(){ this.hasFocus=true; }
}
ionic.tap.handleFocus(ele);
expect( ele.hasFocus ).toEqual(true);
});
it('Should not focus on common elements', function() {
var tags = ['div', 'span', 'i', 'body', 'section', 'article', 'aside', 'li', 'p', 'header', 'button', 'ion-content'];
for(var x=0; x<tags.length; x++) {
var ele = {
tagName: tags[x],
focus: function(){ this.hasFocus=true; }
}
ionic.tap.handleFocus(ele);
expect( ele.hasFocus ).toBeUndefined();
}
});
it('Should not focus on an input that already has focus', function() {
var ele = {
tagName: 'input',
focus: function(){ this.hasFocus=true; }
}
ionic.tap.handleFocus(ele);
expect( ele.hasFocus ).toEqual(true);
ele.focus = function(){ this.hasSecondFocus=true }
ionic.tap.handleFocus(ele);
expect( ele.hasSecondFocus ).toBeUndefined();
});
});

View File

@@ -11,6 +11,7 @@
var startCoordinates = {}; // used to remember where the coordinates of the start of a touch
var clickPreventTimerId;
var _hasTouchScrolled = false; // if the touchmove already exceeded the touchmove tolerance
var _activeElement; // the element which has focus
ionic.tap = {
@@ -74,15 +75,8 @@
ele.dispatchEvent(clickEvent);
if( ele.tagName.match(/input|textarea/i) ) {
ele.focus();
e.preventDefault();
} else if( ele.tagName == 'SELECT' ) {
// select simulateClick should not preventDefault or else no options dialog
ele.focus();
} else {
ionic.tap.blurActive();
}
// if it's an input, focus in on the target, otherwise blur
ionic.tap.handleFocus(ele);
// remember the coordinates of this tap so if it happens again we can ignore it
// but only if the coordinates are not already being actively disabled
@@ -98,7 +92,20 @@
},
ignoreSimulateClick: function(ele) {
return ele.disabled || ele.type === 'file' || ele.type === 'range';
return ele.disabled || ele.type === 'range';
},
handleFocus: function(ele) {
if(ionic.tap.activeElement() !== ele) {
// only set the focus if it doesn't already have it
if( ele.tagName.match(/input|textarea|select/i) ) {
ionic.tap.activeElement(ele);
ele.focus();
} else {
ionic.tap.activeElement(null);
ionic.tap.blurActive();
}
}
},
preventGhostClick: function(e) {
@@ -217,16 +224,22 @@
},
blurActive: function() {
var ele = document.activeElement;
if(ele && (ele.tagName === "INPUT" ||
ele.tagName === "TEXTAREA")) {
// using a timeout to prevent funky scrolling while a keyboard hides
var ele = ionic.tap.activeElement();
if(ele && ele.tagName.match(/input|textarea|select/i) ) {
setTimeout(function(){
ele.blur();
ionic.tap.activeElement(null);
}, 400);
}
},
activeElement: function(ele) {
if(arguments.length) {
_activeElement = ele;
}
return _activeElement || document.activeElement;
},
setTouchStart: function(e) {
_hasTouchScrolled = false;
startCoordinates = ionic.tap.getCoordinates(e);