prevent touchstart when swiping
var touchmoved;
$('button').on('touchend', function(e){
if(touchmoved != true){
// button click action
}
}).on('touchmove', function(e){
touchmoved = true;
}).on('touchstart', function(){
touchmoved = false;
});
What you basically want to do is to detect what is a swipe and what is a click.
We may set some conditions:
- Swipe is when you touch at point
p1
, then move your finger to pointp2
while still having the finger on the screen, then releaseing. - A click is when you tap start tapping and end tapping on the same element.
So, if you store the coordinates of where your touchStart
occured, you can measure the difference at touchEnd
. If the change is large enough, consider it a swipe, otherwise, consider it a click.
Also, if you want to do it really neat, you can also detect which element you are "hovering" over with your finger during a touchMove
, and if you're not still at the element on which you started the click, you can run a clickCancel
method which removes highlights etc.
// grab an element which you can click just as an example
var clickable = document.getElementById("clickableItem"),
// set up some variables that we need for later
currentElement,
clickedElement;
// set up touchStart event handler
var onTouchStart = function(e) {
// store which element we're currently clicking on
clickedElement = this;
// listen to when the user moves finger
this.addEventListener("touchMove" onTouchMove);
// add listener to when touch end occurs
this.addEventListener("touchEnd", onTouchEnd);
};
// when the user swipes, update element positions to swipe
var onTouchMove = function(e) {
// ... do your scrolling here
// store current element
currentElement = document.elementFromPoint(x, y);
// if the current element is no longer the same as we clicked from the beginning, remove highlight
if(clickedElement !== currentElement) {
removeHighlight(clickedElement);
}
};
// this is what is executed when the user stops the movement
var onTouchEnd = function(e) {
if(clickedElement === currentElement) {
removeHighlight(clickedElement);
// .... execute click action
}
// clean up event listeners
this.removeEventListener("touchMove" onTouchMove);
this.removeEventListener("touchEnd", onTouchEnd);
};
function addHighlight(element) {
element.className = "highlighted";
}
function removeHighlight(element) {
element.className = "";
}
clickable.addEventListener("touchStart", onTouchStart);
Then, you will have to add listeners to you scrollable element also, but there you won't have to worry about what happens if the finger has moved inbetween touchStart
and touchEnd
.
var scrollable = document.getElementById("scrollableItem");
// set up touchStart event handler
var onTouchStartScrollable = function(e) {
// listen to when the user moves finger
this.addEventListener("touchMove" onTouchMoveScrollable);
// add listener to when touch end occurs
this.addEventListener("touchEnd", onTouchEndScrollable);
};
// when the user swipes, update element positions to swipe
var onTouchMoveScrollable = function(e) {
// ... do your scrolling here
};
// this is what is executed when the user stops the movement
var onTouchEndScrollable = function(e) {
// clean up event listeners
this.removeEventListener("touchMove" onTouchMoveScrollable);
this.removeEventListener("touchEnd", onTouchEndScrollable);
};
scrollable.addEventListener("touchStart", onTouchStartScrollable);
// Simon A.
Here's what I eventually came up with to allow for a list of items to be scrollable via swipe, but also each item to be 'triggerable' via a tap. In addition, you can still use with a keyboard (using onclick).
I think this is similar to Netlight_Digital_Media's answer. I need to study that one a bit more.
$(document)
// log the position of the touchstart interaction
.bind('touchstart', function(e){
touchStartPos = $(window).scrollTop();
})
// log the position of the touchend interaction
.bind('touchend', function(e){
// calculate how far the page has moved between
// touchstart and end.
var distance = touchStartPos - $(window).scrollTop();
var $clickableItem; // the item I want to be clickable if it's NOT a swipe
// adding this class for devices that
// will trigger a click event after
// the touchend event finishes. This
// tells the click event that we've
// already done things so don't repeat
$clickableItem.addClass("touched");
if (distance > 20 || distance < -20){
// the distance was more than 20px
// so we're assuming they intended
// to swipe to scroll the list and
// not selecting a row.
} else {
// we'll assume it was a tap
whateverFunctionYouWantToTriggerOnTapOrClick()
}
});
$($clickableItem).live('click',function(e){
// for any non-touch device, we need
// to still apply a click event
// but we'll first check to see
// if there was a previous touch
// event by checking for the class
// that was left by the touch event.
if ($(this).hasClass("touched")){
// this item's event was already triggered via touch
// so we won't call the function and reset this for
// the next touch by removing the class
$(this).removeClass("touched");
} else {
// there wasn't a touch event. We're
// instead using a mouse or keyboard
whateverFunctionYouWantToTriggerOnTapOrClick()
}
});
Quoting from DA.:
This is a working example:
var touch_pos;
$(document).on('touchstart', '.action-feature', function(e) {
e.preventDefault();
touch_pos = $(window).scrollTop();
}).on('click touchend', '.action-feature', function(e) {
e.preventDefault();
if(e.type=='touchend' && (Math.abs(touch_pos-$(window).scrollTop())>3)) return;
alert("only accessed when it's a click or not a swipe");
});
Some of these solutions worked for me, but in the end I found that this lightweight library was simpler to setup.
Tocca.js: https://github.com/GianlucaGuarini/Tocca.js
It's quite flexible and detects touch as well as swipe, double-tap etc.