Skip to content

Commit

Permalink
fix(tap): input[file] clicks within ion-content, closes #1237
Browse files Browse the repository at this point in the history
  • Loading branch information
Adam Bradley committed May 1, 2014
1 parent 517658e commit 05a6d7c
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 49 deletions.
2 changes: 1 addition & 1 deletion js/utils/activator.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
38 changes: 19 additions & 19 deletions js/utils/tap.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
},
Expand Down Expand Up @@ -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;
}

};
Expand All @@ -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);

Expand All @@ -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();

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

0 comments on commit 05a6d7c

Please sign in to comment.