I went through the code several times and I cannot find the reason it fails on touch based devices:
/**
* Initialize touch event listener.
*
* @returns {Plugin}
*/
touch: function () {
var self = this;
this._$body.bind('touchstart', function (event) {
var startEvent = event.originalEvent.touches[0];
event.preventDefault();
self._$body.bind('touchmove', function (event) {
var moveEvent = event.originalEvent.touches[0];
var diff = { x: startEvent.clientX - moveEvent.clientX, y: startEvent.clientY - moveEvent.clientY };
var nextStep;
event.preventDefault();
if ((diff.y <= -100 || diff.y >= 100) && Math.abs(diff.y) > Math.abs(diff.x)) {
nextStep = diff.y < 0 ? self._currentStep - 1 : self._currentStep + 1;
self.customScrollTo(nextStep);
}
return false;
});
return false;
});
return this;
},
demo (self signed ssl, don't worry!): https://sandbox.idev.ge/roomshotel/html5_v3/
Problem: Scroll jumps straight to bottom when touch is activated.
Expected result: One touch interaction equals 1 section scrolled.
Any thoughts?
I too think the touchmove
event's callback is being fired on every touch move. By returning false
from that function you only cancel that single touch move event and not all following touch move events.
You cannot use a touchend
event since you want to call self.customScrollTo(nextStep);
as soon as the pointer has travelled 100px.
You want to prevent your touchmove
callback from being executed after the pointer has travelled 100px, this can be done in many ways, ie.
- Using a flag variable like
var trackPointer = true;
, check this flag
each time touchmove
is being triggered and set this flag to false
when the
pointer has travelled 100px.
- When the pointer has travelled 100px,
set the
startEvent
to null
and check this variable on
touchmove
.
- Unbind the
touchmove
event when the pointer has
travelled 100px.
NB: The touchmove
event is being bound each time touchstart
is triggered on this element, these events do not overwrite each other but get stacked! So you might want to consider binding the event only once (ie. on DOM ready) or unbind the event when it's no longer necessary.
The latter is probably the easiest and could be done ie. on touchend
(use namespaces just to be sure to not unbind the same events bound by other scripts):
// Unbind all touchmove.myNameSpace events on touchend.myNameSpace.
self._$body.bind('touchend.myNameSpace').function (event) {
self._$body.unbind('touchmove.myNameSpace');
});
and when the pointer has travelled 100px:
self.customScrollTo(nextStep);
// Unbind all touchmove.myNameSpace events.
self._$body.unbind('touchmove.myNameSpace');
Since 'touchend' is not triggered when the pointer is outside the element (I am not sure about touchmove
), you might also want to unbind right before binding:
event.preventDefault();
// Unbind all touchmove.myNameSpace events and (re)bind touchmove.myNameSpace event.
self._$body.unbind('touchmove.myNameSpace').bind('touchmove.myNameSpace', function (event) {
var moveEvent = event.originalEvent.touches[0];
So you could try (I have not tested it):
/**
* Initialize touch event listener.
*
* @returns {Plugin}
*/
touch: function () {
var self = this;
this._$body.bind('touchstart', function (event) {
var startEvent = event.originalEvent.touches[0];
event.preventDefault();
self._$body.unbind('touchmove.myNameSpace').bind('touchmove.myNameSpace', function (event) {
var moveEvent = event.originalEvent.touches[0];
var diff = { x: startEvent.clientX - moveEvent.clientX, y: startEvent.clientY - moveEvent.clientY };
var nextStep;
event.preventDefault(); // <- Not necessary since you completely cancel the event by returning false.
if ((diff.y <= -100 || diff.y >= 100) && Math.abs(diff.y) > Math.abs(diff.x)) {
nextStep = diff.y < 0 ? self._currentStep - 1 : self._currentStep + 1;
self.customScrollTo(nextStep);
// Unbind all touchmove.myNameSpace events.
self._$body.unbind('touchmove.myNameSpace');
}
return false;
});
return false;
});
// Unbind all touchmove.myNameSpace events on touchend.myNameSpace.
self._$body.bind('touchend.myNameSpace').function (event) {
self._$body.unbind('touchmove.myNameSpace');
});
return this;
},
PS: You might want to use a library like HammerJS (https://github.com/hammerjs/hammer.js) to make gestures work cross browser and also on non-touch devices.
First I will suggest you to use Modernizr to detect touch
if (Modernizr.touch) {
$("body").swipe({
//Generic swipe handler for all directions
swipe: function (event, direction, distance, duration, fingerCount, fingerData) {
if(distance >= 100) //Check the units in px or unit
{
if (direction === "up") {
self.customScrollTo(nextStep);
} else if (direction === "down") {
self.customScrollTo(prevStep);
}
}
}
});
}
I am just putting logic above. "self" is your object only.
for swipe event to work you have to include a jquery plugin https://github.com/mattbryson/TouchSwipe-Jquery-Plugin
I think the event touchmove is firing multiple times, try to use touchend instead.
Regarding to: http://css-tricks.com/the-javascript-behind-touch-friendly-sliders/
I have tested your code with google chrome live javascript edit.
and it worked with the following modification in touchmove event.
setTimeout(function(){ self.customScrollTo(nextStep); }, 250);
/**
* Initialize touch event listener.
*
* @returns {Plugin}
*/
touch: function () {
var self = this;
var flag = false;
this._$body.bind('touchstart', function (event) {
var startEvent = event.originalEvent.touches[0];
self._$body.bind('touchmove', function (event) {
var moveEvent = event.originalEvent.touches[0];
var diff = { x: startEvent.clientX - moveEvent.clientX, y: startEvent.clientY - moveEvent.clientY };
var nextStep;
if (((diff.y <= -100 || diff.y >= 100) && Math.abs(diff.y) > Math.abs(diff.x)) && flag == true) {
nextStep = diff.y < 0 ? self._currentStep - 1 : self._currentStep + 1;
self.customScrollTo(nextStep);
startEvent = event.originalEvent.touches[0]; // Just added this line here.
}
event.preventDefault();
return false;
});
return false;
});
return this;
},
I guess there is a simple edit required.
After you have exceeded the "diff" by 100 or -100 then just update the value of "startEvent" variable. Hence I have added the statement
startEvent = event.originalEvent.touches[0];
So when the event for "self.customScrollTo(nextStep)" is triggered, the "startEvent" value is new value which is the current position of the touched point. And hence when again the touchmove event is called. It will again get a new set of values to calculate.
Please check this code and tell me if this works or not. Even if it is not right.