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

Commit 06a9f0a

Browse files
mgolpetebacondarwin
authored andcommitted
fix(ngTouch): register touches properly when jQuery is used
If jQuery was used with Angular the touch logic was looking for touches under the original event object. However, jQuery wraps all events, keeping the original one under the originalEvent property and copies/normalizes some of event properties. Not all properties are copied, e.g. touches which caused them to not be recognized properly. Thanks to @mcmar & @pomerantsev for original patch ideas. Fixes #4001 Closes #8584 Closes #10797 Closes #11488
1 parent 2cdb201 commit 06a9f0a

File tree

5 files changed

+91
-14
lines changed

5 files changed

+91
-14
lines changed

src/ngScenario/browserTrigger.js

+35
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@
7777
evnt.initAnimationEvent(eventType, null, null, null, eventData.elapsedTime || 0);
7878
}
7979
}
80+
} else if (/touch/.test(eventType) && supportsTouchEvents()) {
81+
evnt = createTouchEvent(element, eventType, x, y);
8082
} else {
8183
evnt = document.createEvent('MouseEvents');
8284
x = x || 0;
@@ -112,4 +114,37 @@
112114

113115
return finalProcessDefault;
114116
};
117+
118+
function supportsTouchEvents() {
119+
if ('_cached' in supportsTouchEvents) {
120+
return supportsTouchEvents._cached;
121+
}
122+
if (!document.createTouch || !document.createTouchList) {
123+
supportsTouchEvents._cached = false;
124+
return false;
125+
}
126+
try {
127+
document.createEvent('TouchEvent');
128+
} catch (e) {
129+
supportsTouchEvents._cached = false;
130+
return false;
131+
}
132+
supportsTouchEvents._cached = true;
133+
return true;
134+
}
135+
136+
function createTouchEvent(element, eventType, x, y) {
137+
var evnt = document.createEvent('TouchEvent');
138+
x = x || 0;
139+
y = y || 0;
140+
141+
var touch = document.createTouch(window, element, Date.now(), x, y, x, y);
142+
var touches = document.createTouchList(touch);
143+
var targetTouches = document.createTouchList(touch);
144+
var changedTouches = document.createTouchList(touch);
145+
146+
evnt.initTouchEvent(eventType, true, true, window, null, 0, 0, 0, 0, false, false, false, false,
147+
touches, targetTouches, changedTouches, 1, 0);
148+
return evnt;
149+
}
115150
}());

src/ngTouch/directive/ngClick.js

+10-5
Original file line numberDiff line numberDiff line change
@@ -221,8 +221,10 @@ ngTouch.directive('ngClick', ['$parse', '$timeout', '$rootElement',
221221

222222
startTime = Date.now();
223223

224-
var touches = event.touches && event.touches.length ? event.touches : [event];
225-
var e = touches[0].originalEvent || touches[0];
224+
// Use jQuery originalEvent
225+
var originalEvent = event.originalEvent || event;
226+
var touches = originalEvent.touches && originalEvent.touches.length ? originalEvent.touches : [originalEvent];
227+
var e = touches[0];
226228
touchStartX = e.clientX;
227229
touchStartY = e.clientY;
228230
});
@@ -238,9 +240,12 @@ ngTouch.directive('ngClick', ['$parse', '$timeout', '$rootElement',
238240
element.on('touchend', function(event) {
239241
var diff = Date.now() - startTime;
240242

241-
var touches = (event.changedTouches && event.changedTouches.length) ? event.changedTouches :
242-
((event.touches && event.touches.length) ? event.touches : [event]);
243-
var e = touches[0].originalEvent || touches[0];
243+
// Use jQuery originalEvent
244+
var originalEvent = event.originalEvent || event;
245+
var touches = (originalEvent.changedTouches && originalEvent.changedTouches.length) ?
246+
originalEvent.changedTouches :
247+
((originalEvent.touches && originalEvent.touches.length) ? originalEvent.touches : [originalEvent]);
248+
var e = touches[0];
244249
var x = e.clientX;
245250
var y = e.clientY;
246251
var dist = Math.sqrt(Math.pow(x - touchStartX, 2) + Math.pow(y - touchStartY, 2));

src/ngTouch/swipe.js

+3-5
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,9 @@ ngTouch.factory('$swipe', [function() {
4040
};
4141

4242
function getCoordinates(event) {
43-
var touches = event.touches && event.touches.length ? event.touches : [event];
44-
var e = (event.changedTouches && event.changedTouches[0]) ||
45-
(event.originalEvent && event.originalEvent.changedTouches &&
46-
event.originalEvent.changedTouches[0]) ||
47-
touches[0].originalEvent || touches[0];
43+
var originalEvent = event.originalEvent || event;
44+
var touches = originalEvent.touches && originalEvent.touches.length ? originalEvent.touches : [originalEvent];
45+
var e = (originalEvent.changedTouches && originalEvent.changedTouches[0]) || touches[0];
4846

4947
return {
5048
x: e.clientX,

test/ngTouch/directive/ngClickSpec.js

+41-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ describe('ngClick (touch)', function() {
55

66
// TODO(braden): Once we have other touch-friendly browsers on CI, allow them here.
77
// Currently Firefox and IE refuse to fire touch events.
8-
var chrome = /chrome/.test(navigator.userAgent.toLowerCase());
9-
if (!chrome) {
8+
// Enable iPhone for manual testing.
9+
if (!/chrome|iphone/i.test(navigator.userAgent)) {
1010
return;
1111
}
1212

@@ -48,6 +48,34 @@ describe('ngClick (touch)', function() {
4848
expect($rootScope.event).toBeDefined();
4949
}));
5050

51+
if (window.jQuery) {
52+
it('should not unwrap a jQuery-wrapped event object on click', inject(function($rootScope, $compile) {
53+
element = $compile('<div ng-click="event = $event"></div>')($rootScope);
54+
$rootScope.$digest();
55+
56+
browserTrigger(element, 'click', {
57+
keys: [],
58+
x: 10,
59+
y: 10
60+
});
61+
expect($rootScope.event.originalEvent).toBeDefined();
62+
expect($rootScope.event.originalEvent.clientX).toBe(10);
63+
expect($rootScope.event.originalEvent.clientY).toBe(10);
64+
}));
65+
66+
it('should not unwrap a jQuery-wrapped event object on touchstart/touchend',
67+
inject(function($rootScope, $compile, $rootElement) {
68+
element = $compile('<div ng-click="event = $event"></div>')($rootScope);
69+
$rootElement.append(element);
70+
$rootScope.$digest();
71+
72+
browserTrigger(element, 'touchstart');
73+
browserTrigger(element, 'touchend');
74+
75+
expect($rootScope.event.originalEvent).toBeDefined();
76+
}));
77+
}
78+
5179

5280
it('should not click if the touch is held too long', inject(function($rootScope, $compile, $rootElement) {
5381
element = $compile('<div ng-click="count = count + 1"></div>')($rootScope);
@@ -463,6 +491,17 @@ describe('ngClick (touch)', function() {
463491

464492
expect($rootScope.selection).toBe('initial');
465493
});
494+
495+
496+
it('should blur the other element on click', function() {
497+
var blurSpy = spyOn(otherElement, 'blur');
498+
touch(otherElement, 10, 10);
499+
500+
time = 500;
501+
click(label, 10, 10);
502+
503+
expect(blurSpy).toHaveBeenCalled();
504+
});
466505
});
467506
});
468507

test/ngTouch/swipeSpec.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,8 @@ describe('$swipe', function() {
6767
if (restrictBrowsers) {
6868
// TODO(braden): Once we have other touch-friendly browsers on CI, allow them here.
6969
// Currently Firefox and IE refuse to fire touch events.
70-
var chrome = /chrome/.test(navigator.userAgent.toLowerCase());
71-
if (!chrome) {
70+
// Enable iPhone for manual testing.
71+
if (!/chrome|iphone/i.test(navigator.userAgent)) {
7272
return;
7373
}
7474
}

0 commit comments

Comments
 (0)