mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
fix(tap): input[file] clicks within ion-content, closes #1237
This commit is contained in:
@@ -14,7 +14,7 @@
|
||||
// when an element is touched/clicked, it climbs up a few
|
||||
// parents to see if it is an .item or .button element
|
||||
ionic.requestAnimationFrame(function(){
|
||||
if (tapRequiresNativeClick(e.target)) return;
|
||||
if ( ionic.tap.requiresNativeClick(e.target) ) return;
|
||||
var ele = e.target;
|
||||
var eleToActivate;
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ ionic.tap = {
|
||||
ignoreScrollStart: function(e) {
|
||||
return (e.defaultPrevented) || // defaultPrevented has been assigned by another component handling the event
|
||||
(e.target.isContentEditable) ||
|
||||
(e.target.type === 'range') ||
|
||||
(/file|range/i).test(e.target.type) ||
|
||||
(e.target.dataset ? e.target.dataset.preventScroll : e.target.getAttribute('data-prevent-default')) == 'true' || // manually set within an elements attributes
|
||||
(!!(/object|embed/i).test(e.target.tagName)); // flash/movie/object touches should not try to scroll
|
||||
},
|
||||
@@ -149,6 +149,22 @@ ionic.tap = {
|
||||
previousInputFocus[x].focus();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
requiresNativeClick: function(ele) {
|
||||
if(!ele || ele.disabled || (/file|range/i).test(ele.type) || (/object|video/i).test(ele.tagName) ) {
|
||||
return true;
|
||||
}
|
||||
if(ele.nodeType === 1) {
|
||||
var element = ele;
|
||||
while(element) {
|
||||
if( (element.dataset ? element.dataset.tapDisabled : element.getAttribute('data-tap-disabled')) == 'true' ) {
|
||||
return true;
|
||||
}
|
||||
element = element.parentElement;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
};
|
||||
@@ -166,7 +182,7 @@ function tapClick(e) {
|
||||
var container = tapContainingElement(e.target);
|
||||
var ele = tapTargetElement(container);
|
||||
|
||||
if( tapRequiresNativeClick(ele) || tapPointerMoved ) return false;
|
||||
if( ionic.tap.requiresNativeClick(ele) || tapPointerMoved ) return false;
|
||||
|
||||
var c = getPointerCoordinates(e);
|
||||
|
||||
@@ -193,7 +209,7 @@ function tapClickGateKeeper(e) {
|
||||
|
||||
// do not allow through any click events that were not created by ionic.tap
|
||||
if( (ionic.scroll.isScrolling && ionic.tap.containsOrIsTextInput(e.target) ) ||
|
||||
(!e.isIonicTap && !tapRequiresNativeClick(e.target)) ) {
|
||||
(!e.isIonicTap && !ionic.tap.requiresNativeClick(e.target)) ) {
|
||||
console.debug('clickPrevent', e.target.tagName);
|
||||
e.stopPropagation();
|
||||
|
||||
@@ -205,22 +221,6 @@ function tapClickGateKeeper(e) {
|
||||
}
|
||||
}
|
||||
|
||||
function tapRequiresNativeClick(ele) {
|
||||
if(!ele || ele.disabled || (/file|range/i).test(ele.type) || (/object|video/i).test(ele.tagName) ) {
|
||||
return true;
|
||||
}
|
||||
if(ele.nodeType === 1) {
|
||||
var element = ele;
|
||||
while(element) {
|
||||
if( (element.dataset ? element.dataset.tapDisabled : element.getAttribute('data-tap-disabled')) == 'true' ) {
|
||||
return true;
|
||||
}
|
||||
element = element.parentElement;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// MOUSE
|
||||
function tapMouseDown(e) {
|
||||
if(e.isIonicTap || tapIgnoreEvent(e)) return;
|
||||
|
||||
@@ -4,8 +4,8 @@ describe('Ionic Element Activator', function() {
|
||||
window.setTimeout = ionic.requestAnimationFrame = function(cb) { cb(); };
|
||||
});
|
||||
|
||||
it('should not active an <a> if tapRequiresNativeClick is true', function() {
|
||||
spyOn(window, 'tapRequiresNativeClick').andReturn(true);
|
||||
it('should not active an <a> if ionic.tap.requiresNativeClick is true', function() {
|
||||
spyOn(ionic.tap, 'requiresNativeClick').andReturn(true);
|
||||
var e = { target: document.createElement('a') };
|
||||
ionic.activator.start(e);
|
||||
expect(e.target.classList.contains('activated')).toEqual(false);
|
||||
@@ -549,60 +549,60 @@ describe('Ionic Tap', function() {
|
||||
expect( tapClick(e) ).toEqual(false);
|
||||
});
|
||||
|
||||
it('Should tapRequiresNativeClick for invalid element', function() {
|
||||
expect( tapRequiresNativeClick( null ) ).toEqual(true);
|
||||
it('Should ionic.tap.requiresNativeClick for invalid element', function() {
|
||||
expect( ionic.tap.requiresNativeClick( null ) ).toEqual(true);
|
||||
});
|
||||
|
||||
it('Should tapRequiresNativeClick for input.disabled', function() {
|
||||
it('Should ionic.tap.requiresNativeClick for input.disabled', function() {
|
||||
var ele = document.createElement('input');
|
||||
ele.disabled = true;
|
||||
expect( tapRequiresNativeClick( ele ) ).toEqual(true);
|
||||
expect( ionic.tap.requiresNativeClick( ele ) ).toEqual(true);
|
||||
});
|
||||
|
||||
it('Should tapRequiresNativeClick for input[range]', function() {
|
||||
it('Should ionic.tap.requiresNativeClick for input[range]', function() {
|
||||
var ele = document.createElement('input');
|
||||
ele.type = 'range';
|
||||
expect( tapRequiresNativeClick( ele ) ).toEqual(true);
|
||||
expect( ionic.tap.requiresNativeClick( ele ) ).toEqual(true);
|
||||
});
|
||||
|
||||
it('Should tapRequiresNativeClick for input[file]', function() {
|
||||
it('Should ionic.tap.requiresNativeClick for input[file]', function() {
|
||||
var ele = document.createElement('input');
|
||||
ele.type = 'file';
|
||||
expect( tapRequiresNativeClick( ele ) ).toEqual(true);
|
||||
expect( ionic.tap.requiresNativeClick( ele ) ).toEqual(true);
|
||||
});
|
||||
|
||||
it('Should tapRequiresNativeClick for video element', function() {
|
||||
it('Should ionic.tap.requiresNativeClick for video element', function() {
|
||||
var ele = document.createElement('video');
|
||||
expect( tapRequiresNativeClick( ele ) ).toEqual(true);
|
||||
expect( ionic.tap.requiresNativeClick( ele ) ).toEqual(true);
|
||||
});
|
||||
|
||||
it('Should tapRequiresNativeClick for object element', function() {
|
||||
it('Should ionic.tap.requiresNativeClick for object element', function() {
|
||||
var ele = document.createElement('object');
|
||||
expect( tapRequiresNativeClick( ele ) ).toEqual(true);
|
||||
expect( ionic.tap.requiresNativeClick( ele ) ).toEqual(true);
|
||||
});
|
||||
|
||||
it('Should not tapRequiresNativeClick for common inputs', function() {
|
||||
it('Should not ionic.tap.requiresNativeClick for common inputs', function() {
|
||||
var inputTypes = ['text', 'email', 'search', 'tel', 'number', 'date', 'month', 'password', null, undefined, ''];
|
||||
for(var x=0; x<inputTypes.length; x++) {
|
||||
var targetEle = document.createElement('input');
|
||||
targetEle.type = inputTypes[x];
|
||||
expect( tapRequiresNativeClick(targetEle) ).toEqual(false);
|
||||
expect( ionic.tap.requiresNativeClick(targetEle) ).toEqual(false);
|
||||
}
|
||||
expect( tapRequiresNativeClick( document.createElement('img') ) ).toEqual(false);
|
||||
expect( tapRequiresNativeClick( document.createElement('div') ) ).toEqual(false);
|
||||
expect( tapRequiresNativeClick( document.createElement('textarea') ) ).toEqual(false);
|
||||
expect( tapRequiresNativeClick( document.createElement('select') ) ).toEqual(false);
|
||||
expect( ionic.tap.requiresNativeClick( document.createElement('img') ) ).toEqual(false);
|
||||
expect( ionic.tap.requiresNativeClick( document.createElement('div') ) ).toEqual(false);
|
||||
expect( ionic.tap.requiresNativeClick( document.createElement('textarea') ) ).toEqual(false);
|
||||
expect( ionic.tap.requiresNativeClick( document.createElement('select') ) ).toEqual(false);
|
||||
});
|
||||
|
||||
it('Should tapRequiresNativeClick for an element with data-tap-disabled attribute', function() {
|
||||
it('Should ionic.tap.requiresNativeClick for an element with data-tap-disabled attribute', function() {
|
||||
var div = document.createElement('div');
|
||||
expect( tapRequiresNativeClick( div ) ).toEqual(false);
|
||||
expect( ionic.tap.requiresNativeClick( div ) ).toEqual(false);
|
||||
|
||||
div.setAttribute('data-tap-disabled', "true");
|
||||
expect( tapRequiresNativeClick( div ) ).toEqual(true);
|
||||
expect( ionic.tap.requiresNativeClick( div ) ).toEqual(true);
|
||||
});
|
||||
|
||||
it('Should tapRequiresNativeClick for an element with one of its parents with data-tap-disabled attribute', function() {
|
||||
it('Should ionic.tap.requiresNativeClick for an element with one of its parents with data-tap-disabled attribute', function() {
|
||||
var div1 = document.createElement('div');
|
||||
var div2 = document.createElement('div');
|
||||
var div3 = document.createElement('div');
|
||||
@@ -616,11 +616,11 @@ describe('Ionic Tap', function() {
|
||||
|
||||
div2.setAttribute('data-tap-disabled', "true");
|
||||
|
||||
expect( tapRequiresNativeClick( div1 ) ).toEqual(false);
|
||||
expect( tapRequiresNativeClick( div2 ) ).toEqual(true);
|
||||
expect( tapRequiresNativeClick( div3 ) ).toEqual(true);
|
||||
expect( tapRequiresNativeClick( div4 ) ).toEqual(true);
|
||||
expect( tapRequiresNativeClick( div5 ) ).toEqual(true);
|
||||
expect( ionic.tap.requiresNativeClick( div1 ) ).toEqual(false);
|
||||
expect( ionic.tap.requiresNativeClick( div2 ) ).toEqual(true);
|
||||
expect( ionic.tap.requiresNativeClick( div3 ) ).toEqual(true);
|
||||
expect( ionic.tap.requiresNativeClick( div4 ) ).toEqual(true);
|
||||
expect( ionic.tap.requiresNativeClick( div5 ) ).toEqual(true);
|
||||
});
|
||||
|
||||
it('Should not allow a click that has an textarea target but not created by tapClick', function() {
|
||||
@@ -861,6 +861,16 @@ describe('Ionic Tap', function() {
|
||||
expect( ionic.tap.ignoreScrollStart(e) ).toEqual(true);
|
||||
});
|
||||
|
||||
it('Should prevent scrolling because the target is input[file]', function() {
|
||||
var target = document.createElement('input');
|
||||
target.type = 'file';
|
||||
var e = {
|
||||
target: target,
|
||||
defaultPrevented: true
|
||||
};
|
||||
expect( ionic.tap.ignoreScrollStart(e) ).toEqual(true);
|
||||
});
|
||||
|
||||
it('Should prevent scrolling because the target is input[range]', function() {
|
||||
var target = document.createElement('input');
|
||||
target.type = 'range';
|
||||
|
||||
Reference in New Issue
Block a user