Skip to content

Commit 4427073

Browse files
mobilabgitpetebacondarwin
authored andcommitted
fix(ngTouch/ngClick): don't bust clicks if the input is inside a label
In our code we have an input element inside a label. The click on the label generates two clicks, but the second click was not recognised by angular-touch as a label click, because it only looks at the current element, instead of looking for a label in parent elements. Therefore, the second click gets busted, and clicking on the label doesn't do anything for 2500ms. Closes angular#11577
1 parent ffb6b2f commit 4427073

File tree

2 files changed

+96
-60
lines changed

2 files changed

+96
-60
lines changed

src/ngTouch/directive/ngClick.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ ngTouch.directive('ngClick', ['$parse', '$timeout', '$rootElement',
144144
lastLabelClickCoordinates = null;
145145
}
146146
// remember label click coordinates to prevent click busting of trigger click event on input
147-
if (nodeName_(event.target) === 'label') {
147+
if (findUpLabel(event.target)) {
148148
lastLabelClickCoordinates = [x, y];
149149
}
150150

@@ -163,6 +163,12 @@ ngTouch.directive('ngClick', ['$parse', '$timeout', '$rootElement',
163163
event.target && event.target.blur && event.target.blur();
164164
}
165165

166+
function findUpLabel(el) {
167+
while (el) {
168+
if (nodeName_(el) === 'label') return true;
169+
el = el.parentNode;
170+
}
171+
}
166172

167173
// Global touchstart handler that creates an allowable region for a click event.
168174
// This allowable region can be removed by preventGhostClick if we want to bust it.

test/ngTouch/directive/ngClickSpec.js

Lines changed: 89 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -409,9 +409,9 @@ describe('ngClick (touch)', function() {
409409
expect($rootScope.count).toBe(2);
410410
}));
411411

412+
describe('near a input and label', function() {
412413

413-
describe('when clicking on a label immediately following a touch event', function() {
414-
var touch = function(element, x, y) {
414+
function touch(element, x, y) {
415415
time = 10;
416416
browserTrigger(element, 'touchstart',{
417417
keys: [],
@@ -425,99 +425,129 @@ describe('ngClick (touch)', function() {
425425
x: x,
426426
y: y
427427
});
428-
};
428+
}
429429

430-
var click = function(element, x, y) {
430+
function click(element, x, y) {
431431
browserTrigger(element, 'click',{
432432
keys: [],
433433
x: x,
434434
y: y
435435
});
436-
};
436+
}
437437

438-
var $rootScope;
439-
var container, otherElement, input, label;
440-
beforeEach(inject(function(_$rootScope_, $compile, $rootElement) {
441-
$rootScope = _$rootScope_;
442-
var container = $compile('<div><div ng-click="count = count + 1"></div>' +
443-
'<input id="input1" type="radio" ng-model="selection" value="radio1">' +
444-
'<label for="input1">Input1</label></div>')($rootScope);
445-
$rootElement.append(container);
446-
otherElement = container.children()[0];
447-
input = container.children()[1];
448-
label = container.children()[2];
438+
describe('immediately following a touch event', function() {
449439

450-
$rootScope.selection = 'initial';
440+
var $rootScope;
441+
var container, otherElement, input, label;
451442

452-
$rootScope.$digest();
453-
}));
443+
beforeEach(inject(function(_$rootScope_, $compile, $rootElement) {
444+
$rootScope = _$rootScope_;
445+
var container = $compile('<div><div ng-click="count = count + 1"></div>' +
446+
'<input id="input1" type="radio" ng-model="selection" value="radio1">' +
447+
'<label for="input1">Input1</label></div>')($rootScope);
448+
$rootElement.append(container);
449+
otherElement = container.children()[0];
450+
input = container.children()[1];
451+
label = container.children()[2];
454452

453+
$rootScope.selection = 'initial';
455454

456-
afterEach(function() {
457-
dealoc(label);
458-
dealoc(input);
459-
dealoc(otherElement);
460-
dealoc(container);
461-
});
455+
$rootScope.$digest();
456+
}));
462457

463458

464-
it('should not cancel input clicks with (0,0) coordinates', function() {
465-
touch(otherElement, 100, 100);
459+
afterEach(function() {
460+
dealoc(container);
461+
});
466462

467-
time = 500;
468-
click(label, 10, 10);
469-
click(input, 0, 0);
470463

471-
expect($rootScope.selection).toBe('radio1');
472-
});
464+
it('should not cancel input clicks with (0,0) coordinates', function() {
465+
touch(otherElement, 100, 100);
473466

467+
time = 500;
468+
click(label, 10, 10);
469+
click(input, 0, 0);
474470

475-
it('should not cancel input clicks with negative coordinates', function() {
476-
touch(otherElement, 100, 100);
471+
expect($rootScope.selection).toBe('radio1');
472+
});
477473

478-
time = 500;
479-
click(label, 10, 10);
480-
click(input, -1, -1);
481474

482-
expect($rootScope.selection).toBe('radio1');
483-
});
475+
it('should not cancel input clicks with negative coordinates', function() {
476+
touch(otherElement, 100, 100);
484477

478+
time = 500;
479+
click(label, 10, 10);
480+
click(input, -1, -1);
485481

486-
it('should not cancel input clicks with positive coordinates identical to label click', function() {
487-
touch(otherElement, 100, 100);
482+
expect($rootScope.selection).toBe('radio1');
483+
});
488484

489-
time = 500;
490-
click(label, 10, 10);
491-
click(input, 10, 10);
492485

493-
expect($rootScope.selection).toBe('radio1');
494-
});
486+
it('should not cancel input clicks with positive coordinates identical to label click', function() {
487+
touch(otherElement, 100, 100);
488+
489+
time = 500;
490+
click(label, 10, 10);
491+
click(input, 10, 10);
492+
493+
expect($rootScope.selection).toBe('radio1');
494+
});
495+
495496

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

497-
it('should cancel input clicks with positive coordinates different than label click', function() {
498-
touch(otherElement, 100, 100);
500+
time = 500;
501+
click(label, 10, 10);
502+
click(input, 11, 11);
499503

500-
time = 500;
501-
click(label, 10, 10);
502-
click(input, 11, 11);
504+
expect($rootScope.selection).toBe('initial');
505+
});
506+
507+
508+
it('should blur the other element on click', function() {
509+
var blurSpy = spyOn(otherElement, 'blur');
510+
touch(otherElement, 10, 10);
503511

504-
expect($rootScope.selection).toBe('initial');
512+
time = 500;
513+
click(label, 10, 10);
514+
515+
expect(blurSpy).toHaveBeenCalled();
516+
});
505517
});
506518

507519

508-
it('should blur the other element on click', function() {
509-
var blurSpy = spyOn(otherElement, 'blur');
510-
touch(otherElement, 10, 10);
520+
describe('where label contains the input', function() {
521+
522+
var $rootScope;
523+
var container, otherElement, input, label;
511524

512-
time = 500;
513-
click(label, 10, 10);
525+
it('should not cancel input clicks with (0,0) coordinates', inject(function($rootScope, $compile, $rootElement) {
526+
var container = $compile('<div><div ng-click="count = count + 1"></div>' +
527+
'<label for="input1">Input1 ' +
528+
'<input id="input1" type="radio" ng-model="selection" value="radio1">' +
529+
'</label></div>')($rootScope);
514530

515-
expect(blurSpy).toHaveBeenCalled();
531+
$rootElement.append(container);
532+
otherElement = container.children()[0];
533+
label = container.children().eq(1);
534+
input = label.children()[0];
535+
536+
$rootScope.selection = 'initial';
537+
$rootScope.$digest();
538+
539+
touch(otherElement, 100, 100);
540+
click(input, 0, 0);
541+
click(label, 10, 10);
542+
543+
expect($rootScope.selection).toBe('radio1');
544+
545+
dealoc(container);
546+
}));
516547
});
517548
});
518549
});
519550

520-
521551
describe('click fallback', function() {
522552

523553
it('should treat a click as a tap on desktop', inject(function($rootScope, $compile) {

0 commit comments

Comments
 (0)