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

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 #4001
Closes #8584
Closes #10797
Closes #11488
  • Loading branch information
mgol authored and petebacondarwin committed Apr 2, 2015
1 parent 1f65087 commit 40441f6
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 40441f6

Please sign in to comment.