Skip to content

Commit

Permalink
fix(ngTouch): register touches properly when jQuery is used
Browse files Browse the repository at this point in the history
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 angular#4001
Closes angular#8584
Closes angular#10797
  • Loading branch information
mgol authored and petebacondarwin committed Apr 2, 2015
1 parent 2cdb201 commit 80a31f8
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 14 deletions.
35 changes: 35 additions & 0 deletions src/ngScenario/browserTrigger.js
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
}());
15 changes: 10 additions & 5 deletions src/ngTouch/directive/ngClick.js
Expand Up @@ -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;
});
Expand All @@ -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));
Expand Down
8 changes: 3 additions & 5 deletions src/ngTouch/swipe.js
Expand Up @@ -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,
Expand Down
43 changes: 41 additions & 2 deletions test/ngTouch/directive/ngClickSpec.js
Expand Up @@ -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;
}

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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();
});
});
});

Expand Down
4 changes: 2 additions & 2 deletions test/ngTouch/swipeSpec.js
Expand Up @@ -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;
}
}
Expand Down

0 comments on commit 80a31f8

Please sign in to comment.