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

Commit 01a34f5

Browse files
cconstantintbosch
authored andcommitted
fix(ngTouch): update workaround for desktop Webkit quirk
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
1 parent 916e53c commit 01a34f5

File tree

2 files changed

+98
-24
lines changed

2 files changed

+98
-24
lines changed

src/ngTouch/directive/ngClick.js

+15-1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ ngTouch.directive('ngClick', ['$parse', '$timeout', '$rootElement',
5353
var ACTIVE_CLASS_NAME = 'ng-click-active';
5454
var lastPreventedTime;
5555
var touchCoordinates;
56+
var lastLabelClickCoordinates;
5657

5758

5859
// TAP EVENTS AND GHOST CLICKS
@@ -124,10 +125,23 @@ ngTouch.directive('ngClick', ['$parse', '$timeout', '$rootElement',
124125
var y = touches[0].clientY;
125126
// Work around desktop Webkit quirk where clicking a label will fire two clicks (on the label
126127
// and on the input element). Depending on the exact browser, this second click we don't want
127-
// to bust has either (0,0) or negative coordinates.
128+
// to bust has either (0,0), negative coordinates, or coordinates equal to triggering label
129+
// click event
128130
if (x < 1 && y < 1) {
129131
return; // offscreen
130132
}
133+
if (lastLabelClickCoordinates &&
134+
lastLabelClickCoordinates[0] === x && lastLabelClickCoordinates[1] === y) {
135+
return; // input click triggered by label click
136+
}
137+
// reset label click coordinates on first subsequent click
138+
if (lastLabelClickCoordinates) {
139+
lastLabelClickCoordinates = null;
140+
}
141+
// remember label click coordinates to prevent click busting of trigger click event on input
142+
if (event.target.tagName.toLowerCase() === 'label') {
143+
lastLabelClickCoordinates = [x, y];
144+
}
131145

132146
// Look for an allowable region containing this click.
133147
// If we find one, that means it was created by touchstart and not removed by

test/ngTouch/directive/ngClickSpec.js

+83-23
Original file line numberDiff line numberDiff line change
@@ -370,40 +370,100 @@ describe('ngClick (touch)', function() {
370370
}));
371371

372372

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

376-
$rootScope.count = 0;
382+
time = 50;
383+
browserTrigger(element, 'touchend',{
384+
keys: [],
385+
x: x,
386+
y: y
387+
});
388+
};
377389

378-
$rootScope.$digest();
390+
var click = function(element, x, y) {
391+
browserTrigger(element, 'click',{
392+
keys: [],
393+
x: x,
394+
y: y
395+
});
396+
};
379397

380-
expect($rootScope.count).toBe(0);
398+
var $rootScope;
399+
var container, otherElement, input, label;
400+
beforeEach(inject(function(_$rootScope_, $compile, $rootElement) {
401+
$rootScope = _$rootScope_;
402+
var container = $compile('<div><div ng-click="count = count + 1"></div>' +
403+
'<input id="input1" type="radio" ng-model="selection" value="radio1">' +
404+
'<label for="input1">Input1</label></div>')($rootScope);
405+
$rootElement.append(container);
406+
otherElement = container.children()[0];
407+
input = container.children()[1];
408+
label = container.children()[2];
381409

382-
time = 10;
383-
browserTrigger(element1, 'touchstart',{
384-
keys: [],
385-
x: 10,
386-
y: 10
410+
$rootScope.selection = 'initial';
411+
412+
$rootScope.$digest();
413+
}));
414+
415+
416+
afterEach(function() {
417+
dealoc(label);
418+
dealoc(input);
419+
dealoc(otherElement);
420+
dealoc(container);
387421
});
388422

389-
time = 50;
390-
browserTrigger(element1, 'touchend',{
391-
keys: [],
392-
x: 10,
393-
y: 10
423+
424+
it('should not cancel input clicks with (0,0) coordinates', function() {
425+
touch(otherElement, 100, 100);
426+
427+
time = 500;
428+
click(label, 10, 10);
429+
click(input, 0, 0);
430+
431+
expect($rootScope.selection).toBe('radio1');
394432
});
395433

396-
expect($rootScope.count).toBe(1);
397434

398-
time = 2700;
399-
browserTrigger(element1, 'click',{
400-
keys: [],
401-
x: 10,
402-
y: 10
435+
it('should not cancel input clicks with negative coordinates', function() {
436+
touch(otherElement, 100, 100);
437+
438+
time = 500;
439+
click(label, 10, 10);
440+
click(input, -1, -1);
441+
442+
expect($rootScope.selection).toBe('radio1');
403443
});
404444

405-
expect($rootScope.count).toBe(2);
406-
}));
445+
446+
it('should not cancel input clicks with positive coordinates identical to label click', function() {
447+
touch(otherElement, 100, 100);
448+
449+
time = 500;
450+
click(label, 10, 10);
451+
click(input, 10, 10);
452+
453+
expect($rootScope.selection).toBe('radio1');
454+
});
455+
456+
457+
it('should cancel input clicks with positive coordinates different than label click', function() {
458+
touch(otherElement, 100, 100);
459+
460+
time = 500;
461+
click(label, 10, 10);
462+
click(input, 11, 11);
463+
464+
expect($rootScope.selection).toBe('initial');
465+
});
466+
});
407467
});
408468

409469

0 commit comments

Comments
 (0)