diff --git a/src/definitions/modules/slider.js b/src/definitions/modules/slider.js index 77c8cbef9b..ffe4e3bfbf 100644 --- a/src/definitions/modules/slider.js +++ b/src/definitions/modules/slider.js @@ -70,6 +70,7 @@ $.fn.slider = function(parameters) { $module = $(this), $currThumb, + touchIdentifier, $thumb, $secondThumb, $track, @@ -86,7 +87,6 @@ $.fn.slider = function(parameters) { secondPos, offset, precision, - isTouch, gapRatio = 1, previousValue, @@ -104,7 +104,6 @@ $.fn.slider = function(parameters) { currentRange += 1; documentEventID = currentRange; - isTouch = module.setup.testOutTouch(); module.setup.layout(); module.setup.labels(); @@ -175,14 +174,6 @@ $.fn.slider = function(parameters) { } } }, - testOutTouch: function() { - try { - document.createEvent('TouchEvent'); - return true; - } catch (e) { - return false; - } - }, customLabel: function() { var $children = $labels.find('.label'), @@ -236,9 +227,6 @@ $.fn.slider = function(parameters) { module.bind.globalKeyboardEvents(); module.bind.keyboardEvents(); module.bind.mouseEvents(); - if(module.is.touch()) { - module.bind.touchEvents(); - } if (settings.autoAdjustLabels) { module.bind.windowEvents(); } @@ -251,7 +239,7 @@ $.fn.slider = function(parameters) { $(document).on('keydown' + eventNamespace + documentEventID, module.event.activateFocus); }, mouseEvents: function() { - module.verbose('Binding mouse events'); + module.verbose('Binding mouse and touch events'); $module.find('.track, .thumb, .inner').on('mousedown' + eventNamespace, function(event) { event.stopImmediatePropagation(); event.preventDefault(); @@ -264,27 +252,20 @@ $.fn.slider = function(parameters) { $module.on('mouseleave' + eventNamespace, function(event) { isHover = false; }); - }, - touchEvents: function() { - module.verbose('Binding touch events'); - $module.find('.track, .thumb, .inner').on('touchstart' + eventNamespace, function(event) { - event.stopImmediatePropagation(); - event.preventDefault(); - module.event.down(event); - }); - $module.on('touchstart' + eventNamespace, module.event.down); + // All touch events are invoked on the element where the touch *started*. Thus, we can bind them all + // on the thumb(s) and don't need to worry about interference with other components, i.e. no dynamic binding + // and unbinding required. + $module.find('.thumb') + .on('touchstart' + eventNamespace, module.event.touchDown) + .on('touchmove' + eventNamespace, module.event.move) + .on('touchend' + eventNamespace, module.event.up) + .on('touchcancel' + eventNamespace, module.event.touchCancel); }, slidingEvents: function() { // these don't need the identifier because we only ever want one of them to be registered with document module.verbose('Binding page wide events while handle is being draged'); - if(module.is.touch()) { - $(document).on('touchmove' + eventNamespace, module.event.move); - $(document).on('touchend' + eventNamespace, module.event.up); - } - else { - $(document).on('mousemove' + eventNamespace, module.event.move); - $(document).on('mouseup' + eventNamespace, module.event.up); - } + $(document).on('mousemove' + eventNamespace, module.event.move); + $(document).on('mouseup' + eventNamespace, module.event.up); }, windowEvents: function() { $window.on('resize' + eventNamespace, module.event.resize); @@ -294,24 +275,22 @@ $.fn.slider = function(parameters) { unbind: { events: function() { $module.find('.track, .thumb, .inner').off('mousedown' + eventNamespace); - $module.find('.track, .thumb, .inner').off('touchstart' + eventNamespace); $module.off('mousedown' + eventNamespace); $module.off('mouseenter' + eventNamespace); $module.off('mouseleave' + eventNamespace); - $module.off('touchstart' + eventNamespace); + $module.find('.thumb') + .off('touchstart' + eventNamespace) + .off('touchmove' + eventNamespace) + .off('touchend' + eventNamespace) + .off('touchcancel' + eventNamespace); $module.off('keydown' + eventNamespace); $module.off('focusout' + eventNamespace); $(document).off('keydown' + eventNamespace + documentEventID, module.event.activateFocus); $window.off('resize' + eventNamespace); }, slidingEvents: function() { - if(module.is.touch()) { - $(document).off('touchmove' + eventNamespace); - $(document).off('touchend' + eventNamespace); - } else { - $(document).off('mousemove' + eventNamespace); - $(document).off('mouseup' + eventNamespace); - } + $(document).off('mousemove' + eventNamespace); + $(document).off('mouseup' + eventNamespace); }, }, @@ -341,10 +320,31 @@ $.fn.slider = function(parameters) { module.bind.slidingEvents(); } }, + touchDown: function(event) { + event.preventDefault(); // disable mouse emulation and touch-scrolling + event.stopImmediatePropagation(); + if(touchIdentifier !== undefined) { + // ignore multiple touches on the same slider -- + // we cannot handle changing both thumbs at once due to shared state + return; + } + $currThumb = $(event.target); + var touchEvent = event.touches ? event : event.originalEvent; + touchIdentifier = touchEvent.targetTouches[0].identifier; + if(previousValue === undefined) { + previousValue = module.get.currentThumbValue(); + } + }, move: function(event) { - event.preventDefault(); + if(event.type == 'mousemove') { + event.preventDefault(); // prevent text selection etc. + } + if(module.is.disabled()) { + // touch events are always bound, so we need to prevent touch-sliding on disabled sliders here + return; + } var value = module.determine.valueFromEvent(event); - if($currThumb === undefined) { + if(event.type == 'mousemove' && $currThumb === undefined) { var eventPos = module.determine.eventPos(event), newPos = module.determine.pos(eventPos) @@ -381,13 +381,26 @@ $.fn.slider = function(parameters) { }, up: function(event) { event.preventDefault(); + if(module.is.disabled()) { + // touch events are always bound, so we need to prevent touch-sliding on disabled sliders here + return; + } var value = module.determine.valueFromEvent(event); module.set.value(value); module.unbind.slidingEvents(); + touchIdentifier = undefined; if (previousValue !== undefined) { previousValue = undefined; } }, + touchCancel: function(event) { + event.preventDefault(); + touchIdentifier = undefined; + if (previousValue !== undefined) { + module.update.value(previousValue); + previousValue = undefined; + } + }, keydown: function(event, first) { if(settings.preventCrossover && module.is.range() && module.thumbVal === module.secondThumbVal) { $currThumb = undefined; @@ -500,9 +513,6 @@ $.fn.slider = function(parameters) { }, smooth: function() { return settings.smooth || $module.hasClass(settings.className.smooth); - }, - touch: function() { - return isTouch; } }, @@ -766,12 +776,19 @@ $.fn.slider = function(parameters) { return value; }, eventPos: function(event) { - if(module.is.touch()) { + if(event.type === "touchmove" || event.type === "touchend") { + var + touchEvent = event.touches ? event : event.originalEvent, + touch = touchEvent.changedTouches[0]; // fall back to first touch if correct touch not found + for(var i=0; i < touchEvent.touches.length; i++) { + if(touchEvent.touches[i].identifier === touchIdentifier) { + touch = touchEvent.touches[i]; + break; + } + } var - touchEvent = event.changedTouches ? event : event.originalEvent, - touches = touchEvent.changedTouches[0] ? touchEvent.changedTouches : touchEvent.touches, - touchY = touches[0].pageY, - touchX = touches[0].pageX + touchY = touch.pageY, + touchX = touch.pageX ; return module.is.vertical() ? touchY : touchX; }