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
This commit is contained in:
committed by
Peter Bacon Darwin
parent
2cdb2016b9
commit
06a9f0a95f
@@ -77,6 +77,8 @@
|
||||
evnt.initAnimationEvent(eventType, null, null, null, eventData.elapsedTime || 0);
|
||||
}
|
||||
}
|
||||
} else if (/touch/.test(eventType) && supportsTouchEvents()) {
|
||||
evnt = createTouchEvent(element, eventType, x, y);
|
||||
} else {
|
||||
evnt = document.createEvent('MouseEvents');
|
||||
x = x || 0;
|
||||
@@ -112,4 +114,37 @@
|
||||
|
||||
return finalProcessDefault;
|
||||
};
|
||||
|
||||
function supportsTouchEvents() {
|
||||
if ('_cached' in supportsTouchEvents) {
|
||||
return supportsTouchEvents._cached;
|
||||
}
|
||||
if (!document.createTouch || !document.createTouchList) {
|
||||
supportsTouchEvents._cached = false;
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
document.createEvent('TouchEvent');
|
||||
} catch (e) {
|
||||
supportsTouchEvents._cached = false;
|
||||
return false;
|
||||
}
|
||||
supportsTouchEvents._cached = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
function createTouchEvent(element, eventType, x, y) {
|
||||
var evnt = document.createEvent('TouchEvent');
|
||||
x = x || 0;
|
||||
y = y || 0;
|
||||
|
||||
var touch = document.createTouch(window, element, Date.now(), x, y, x, y);
|
||||
var touches = document.createTouchList(touch);
|
||||
var targetTouches = document.createTouchList(touch);
|
||||
var changedTouches = document.createTouchList(touch);
|
||||
|
||||
evnt.initTouchEvent(eventType, true, true, window, null, 0, 0, 0, 0, false, false, false, false,
|
||||
touches, targetTouches, changedTouches, 1, 0);
|
||||
return evnt;
|
||||
}
|
||||
}());
|
||||
|
||||
@@ -221,8 +221,10 @@ ngTouch.directive('ngClick', ['$parse', '$timeout', '$rootElement',
|
||||
|
||||
startTime = Date.now();
|
||||
|
||||
var touches = event.touches && event.touches.length ? event.touches : [event];
|
||||
var e = touches[0].originalEvent || touches[0];
|
||||
// Use jQuery originalEvent
|
||||
var originalEvent = event.originalEvent || event;
|
||||
var touches = originalEvent.touches && originalEvent.touches.length ? originalEvent.touches : [originalEvent];
|
||||
var e = touches[0];
|
||||
touchStartX = e.clientX;
|
||||
touchStartY = e.clientY;
|
||||
});
|
||||
@@ -238,9 +240,12 @@ ngTouch.directive('ngClick', ['$parse', '$timeout', '$rootElement',
|
||||
element.on('touchend', function(event) {
|
||||
var diff = Date.now() - startTime;
|
||||
|
||||
var touches = (event.changedTouches && event.changedTouches.length) ? event.changedTouches :
|
||||
((event.touches && event.touches.length) ? event.touches : [event]);
|
||||
var e = touches[0].originalEvent || touches[0];
|
||||
// Use jQuery originalEvent
|
||||
var originalEvent = event.originalEvent || event;
|
||||
var touches = (originalEvent.changedTouches && originalEvent.changedTouches.length) ?
|
||||
originalEvent.changedTouches :
|
||||
((originalEvent.touches && originalEvent.touches.length) ? originalEvent.touches : [originalEvent]);
|
||||
var e = touches[0];
|
||||
var x = e.clientX;
|
||||
var y = e.clientY;
|
||||
var dist = Math.sqrt(Math.pow(x - touchStartX, 2) + Math.pow(y - touchStartY, 2));
|
||||
|
||||
@@ -40,11 +40,9 @@ ngTouch.factory('$swipe', [function() {
|
||||
};
|
||||
|
||||
function getCoordinates(event) {
|
||||
var touches = event.touches && event.touches.length ? event.touches : [event];
|
||||
var e = (event.changedTouches && event.changedTouches[0]) ||
|
||||
(event.originalEvent && event.originalEvent.changedTouches &&
|
||||
event.originalEvent.changedTouches[0]) ||
|
||||
touches[0].originalEvent || touches[0];
|
||||
var originalEvent = event.originalEvent || event;
|
||||
var touches = originalEvent.touches && originalEvent.touches.length ? originalEvent.touches : [originalEvent];
|
||||
var e = (originalEvent.changedTouches && originalEvent.changedTouches[0]) || touches[0];
|
||||
|
||||
return {
|
||||
x: e.clientX,
|
||||
|
||||
@@ -5,8 +5,8 @@ describe('ngClick (touch)', function() {
|
||||
|
||||
// TODO(braden): Once we have other touch-friendly browsers on CI, allow them here.
|
||||
// Currently Firefox and IE refuse to fire touch events.
|
||||
var chrome = /chrome/.test(navigator.userAgent.toLowerCase());
|
||||
if (!chrome) {
|
||||
// Enable iPhone for manual testing.
|
||||
if (!/chrome|iphone/i.test(navigator.userAgent)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -48,6 +48,34 @@ describe('ngClick (touch)', function() {
|
||||
expect($rootScope.event).toBeDefined();
|
||||
}));
|
||||
|
||||
if (window.jQuery) {
|
||||
it('should not unwrap a jQuery-wrapped event object on click', inject(function($rootScope, $compile) {
|
||||
element = $compile('<div ng-click="event = $event"></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
|
||||
browserTrigger(element, 'click', {
|
||||
keys: [],
|
||||
x: 10,
|
||||
y: 10
|
||||
});
|
||||
expect($rootScope.event.originalEvent).toBeDefined();
|
||||
expect($rootScope.event.originalEvent.clientX).toBe(10);
|
||||
expect($rootScope.event.originalEvent.clientY).toBe(10);
|
||||
}));
|
||||
|
||||
it('should not unwrap a jQuery-wrapped event object on touchstart/touchend',
|
||||
inject(function($rootScope, $compile, $rootElement) {
|
||||
element = $compile('<div ng-click="event = $event"></div>')($rootScope);
|
||||
$rootElement.append(element);
|
||||
$rootScope.$digest();
|
||||
|
||||
browserTrigger(element, 'touchstart');
|
||||
browserTrigger(element, 'touchend');
|
||||
|
||||
expect($rootScope.event.originalEvent).toBeDefined();
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
it('should not click if the touch is held too long', inject(function($rootScope, $compile, $rootElement) {
|
||||
element = $compile('<div ng-click="count = count + 1"></div>')($rootScope);
|
||||
@@ -463,6 +491,17 @@ describe('ngClick (touch)', function() {
|
||||
|
||||
expect($rootScope.selection).toBe('initial');
|
||||
});
|
||||
|
||||
|
||||
it('should blur the other element on click', function() {
|
||||
var blurSpy = spyOn(otherElement, 'blur');
|
||||
touch(otherElement, 10, 10);
|
||||
|
||||
time = 500;
|
||||
click(label, 10, 10);
|
||||
|
||||
expect(blurSpy).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -67,8 +67,8 @@ describe('$swipe', function() {
|
||||
if (restrictBrowsers) {
|
||||
// TODO(braden): Once we have other touch-friendly browsers on CI, allow them here.
|
||||
// Currently Firefox and IE refuse to fire touch events.
|
||||
var chrome = /chrome/.test(navigator.userAgent.toLowerCase());
|
||||
if (!chrome) {
|
||||
// Enable iPhone for manual testing.
|
||||
if (!/chrome|iphone/i.test(navigator.userAgent)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user