Skip to content
This repository has been archived by the owner on Apr 12, 2024. It is now read-only.

Commit

Permalink
fix(ngTouch): update workaround for desktop Webkit quirk
Browse files Browse the repository at this point in the history
Fix click busting of input click triggered by a label click quickly
following a touch event on a different element, in desktop
and mobile WebKit

To reproduce the issue fixed by this commit set up a page with
 - an element with ng-click
 - a radio button (with hg-model) and associated label
In a quick sequence tap on the element and then on the label.
The radio button will not be checked, unless PREVENT_DURATION has passed

Closes #6302
  • Loading branch information
cconstantin authored and tbosch committed Mar 21, 2014
1 parent 916e53c commit 01a34f5
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 24 deletions.
16 changes: 15 additions & 1 deletion src/ngTouch/directive/ngClick.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ ngTouch.directive('ngClick', ['$parse', '$timeout', '$rootElement',
var ACTIVE_CLASS_NAME = 'ng-click-active';
var lastPreventedTime;
var touchCoordinates;
var lastLabelClickCoordinates;


// TAP EVENTS AND GHOST CLICKS
Expand Down Expand Up @@ -124,10 +125,23 @@ ngTouch.directive('ngClick', ['$parse', '$timeout', '$rootElement',
var y = touches[0].clientY;
// Work around desktop Webkit quirk where clicking a label will fire two clicks (on the label
// and on the input element). Depending on the exact browser, this second click we don't want
// to bust has either (0,0) or negative coordinates.
// to bust has either (0,0), negative coordinates, or coordinates equal to triggering label
// click event
if (x < 1 && y < 1) {
return; // offscreen
}
if (lastLabelClickCoordinates &&
lastLabelClickCoordinates[0] === x && lastLabelClickCoordinates[1] === y) {
return; // input click triggered by label click
}
// reset label click coordinates on first subsequent click
if (lastLabelClickCoordinates) {
lastLabelClickCoordinates = null;
}
// remember label click coordinates to prevent click busting of trigger click event on input
if (event.target.tagName.toLowerCase() === 'label') {
lastLabelClickCoordinates = [x, y];
}

// Look for an allowable region containing this click.
// If we find one, that means it was created by touchstart and not removed by
Expand Down
106 changes: 83 additions & 23 deletions test/ngTouch/directive/ngClickSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -370,40 +370,100 @@ describe('ngClick (touch)', function() {
}));


it('should not cancel clicks that come long after', inject(function($rootScope, $compile) {
element1 = $compile('<div ng-click="count = count + 1"></div>')($rootScope);
describe('when clicking on a label immediately following a touch event', function() {
var touch = function(element, x, y) {
time = 10;
browserTrigger(element, 'touchstart',{
keys: [],
x: x,
y: y
});

$rootScope.count = 0;
time = 50;
browserTrigger(element, 'touchend',{
keys: [],
x: x,
y: y
});
};

$rootScope.$digest();
var click = function(element, x, y) {
browserTrigger(element, 'click',{
keys: [],
x: x,
y: y
});
};

expect($rootScope.count).toBe(0);
var $rootScope;
var container, otherElement, input, label;
beforeEach(inject(function(_$rootScope_, $compile, $rootElement) {
$rootScope = _$rootScope_;
var container = $compile('<div><div ng-click="count = count + 1"></div>' +
'<input id="input1" type="radio" ng-model="selection" value="radio1">' +
'<label for="input1">Input1</label></div>')($rootScope);
$rootElement.append(container);
otherElement = container.children()[0];
input = container.children()[1];
label = container.children()[2];

time = 10;
browserTrigger(element1, 'touchstart',{
keys: [],
x: 10,
y: 10
$rootScope.selection = 'initial';

$rootScope.$digest();
}));


afterEach(function() {
dealoc(label);
dealoc(input);
dealoc(otherElement);
dealoc(container);
});

time = 50;
browserTrigger(element1, 'touchend',{
keys: [],
x: 10,
y: 10

it('should not cancel input clicks with (0,0) coordinates', function() {
touch(otherElement, 100, 100);

time = 500;
click(label, 10, 10);
click(input, 0, 0);

expect($rootScope.selection).toBe('radio1');
});

expect($rootScope.count).toBe(1);

time = 2700;
browserTrigger(element1, 'click',{
keys: [],
x: 10,
y: 10
it('should not cancel input clicks with negative coordinates', function() {
touch(otherElement, 100, 100);

time = 500;
click(label, 10, 10);
click(input, -1, -1);

expect($rootScope.selection).toBe('radio1');
});

expect($rootScope.count).toBe(2);
}));

it('should not cancel input clicks with positive coordinates identical to label click', function() {
touch(otherElement, 100, 100);

time = 500;
click(label, 10, 10);
click(input, 10, 10);

expect($rootScope.selection).toBe('radio1');
});


it('should cancel input clicks with positive coordinates different than label click', function() {
touch(otherElement, 100, 100);

time = 500;
click(label, 10, 10);
click(input, 11, 11);

expect($rootScope.selection).toBe('initial');
});
});
});


Expand Down

0 comments on commit 01a34f5

Please sign in to comment.