滑动时防止touchstart

时间:2011-08-15 19:08:33

标签: javascript jquery scroll touch touch-event

我在移动设备上有可滚动的列表。他们希望人们能够通过滑动滚动列表,并通过点击选择一行。

抓住了两者的结合。如果您实际滚动列表,我不希望选择一行。这是我发现的:

滚动时不会触发:

  • 点击
  • mouseup

滚动时会触发:

  • mousedown
  • touchstart
  • touchend

简单的解决方案就是坚持点击事件。但我们发现,在某些黑莓设备上,touchstart之间存在非常明显的延迟,然后触发点击或鼠标。这种延迟足以使它在这些设备上无法使用。

因此,我们留下了其他选择。但是,使用这些选项,您可以滚动列表而不触发您触摸的行以开始滚动。

此处解决此问题的最佳做法是什么?

11 个答案:

答案 0 :(得分:44)

var touchmoved;
$('button').on('touchend', function(e){
    if(touchmoved != true){
        // button click action
    }
}).on('touchmove', function(e){
    touchmoved = true;
}).on('touchstart', function(){
    touchmoved = false;
});

答案 1 :(得分:22)

您基本上想要做的是检测什么是滑动以及什么是点击。

我们可能会设置一些条件:

  1. 当您在p1点触摸时滑动,然后将手指移动到指向p2,同时仍然将手指放在屏幕上,然后释放。
  2. 点击是指点击开始点击并结束点击同一元素。
  3. 因此,如果您存储touchStart发生位置的坐标,则可以在touchEnd处衡量差异。如果更改足够大,请将其视为滑动,否则,请将其视为单击。

    另外,如果你想要做得非常整洁,你还可以在touchMove期间用手指检测到你正在“悬停”的元素,如果你还不在你的元素上启动点击后,您可以运行clickCancel方法删除突出显示等。

    // 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);
    

    然后,您还必须向您添加可滚动元素的侦听器,但是如果手指在touchStarttouchEnd之间移动,您将不必担心会发生什么。

    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。

答案 2 :(得分:17)

这是我最终提出的允许通过滑动滚动项目的列表,但每个项目都可以通过点击“触发”。此外,您仍然可以使用键盘(使用onclick)。

我认为这类似于Netlight_Digital_Media的回答。我需要多研究一下。

$(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()
}
});

答案 3 :(得分:6)

引用DA。:

这是一个有效的例子:

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");
});

答案 4 :(得分:3)

其中一些解决方案对我有用,但最后我发现这个轻量级库设置起来比较简单。

Tocca.js:https://github.com/GianlucaGuarini/Tocca.js

它非常灵活,可以检测触摸以及滑动,双击等。

答案 5 :(得分:2)

我遇到了同样的问题,这是一个适合我的快速解决方案

$(document).on('touchstart', 'button', function(evt){ 
    var oldScrollTop = $(window).scrollTop();
    window.setTimeout( function() {
        var newScrollTop = $(window).scrollTop();
        if (Math.abs(oldScrollTop-newScrollTop)<3) $button.addClass('touchactive');
    }, 200);
});

基本上不是立即处理touchstart,而是等待几毫秒(本例中为200ms),然后检查滚动位置,更改了scrollposition,然后我们不需要处理touchstart。

答案 6 :(得分:1)

我遇到了这个优雅的解决方案,就像使用jQuery的魅力一样。我的问题是阻止列表项在滚动期间调用其触摸启动事件。这也适用于滑动。

  1. 将touchstart绑定到将使用类“listObject”滚动或滑动的每个项目

    $('.listObject').live('touchstart', touchScroll);
    
  2. 然后为每个项目分配一个数据对象attr,定义要调用的函数

    <button class='listObject' data-object=alert('You are alerted !')>Alert Me</button>
    
  3. 以下功能可以有效地区分点击和滚动或滑动。

    function touchScroll(e){
    
        var objTarget = $(event.target);
    
        if(objTarget.attr('data-object')){
            var fn = objTarget.attr('data-object'); //function to call if tapped    
        }   
    
        if(!touchEnabled){// default if not touch device
            eval(fn);
            console.log("clicked", 1);
            return;
        }
    
        $(e.target).on('touchend', function(e){
            eval(fn); //trigger the function
            console.log("touchEnd")      
            $(e.target).off('touchend');
        });
    
        $(e.target).on('touchmove', function(e){
            $(e.target).off('touchend');
            console.log("moved")
        }); 
    
    }
    

答案 7 :(得分:0)

我做了一些不同的工作。它绝对不是很优雅,当然不适合大多数情况,但它对我有用。

我一直在使用jQuery的toggleSlide()打开和关闭输入div,在touchstart上触发幻灯片。问题是当用户想要滚动时,触摸的div会打开。为了防止这种情况发生,(或在用户注意到之前将其反转)我在文档中添加了一个touchslide事件,该事件将关闭最后一个触摸的div。

更深入地说,这是一段代码片段:

var lastTouched;

document.addEventListener('touchmove',function(){
    lastTouched.hide();
});

$('#button').addEventListener('touchstart',function(){
    $('#slide').slideToggle();
    lastTouched = $('#slide');
});

全局变量存储最后一次触摸的div,如果用户滑动,则document.touchmove事件会隐藏该div。有时候你会看到一个闪烁的部分,但它适用于我需要的东西,并且很容易让我想出来。

答案 8 :(得分:0)

我使用这段代码,这样只有在没有被刷过的情况下触发按钮(在touchend上):

var startY;
var yDistance;

function touchHandler(event) {
    touch = event.changedTouches[0];
    event.preventDefault();
}

$('.button').on("touchstart", touchHandler, true);
$('.button').on("touchmove", touchHandler, true);

$('.button').on("touchstart", function(){
    startY = touch.clientY;
});

$('.button').on('touchend', function(){

    yDistance = startY - touch.clientY;

    if(Math.abs(yDist) < 30){

        //button response here, only if user is not swiping
        console.log("button pressed")
    }
});

答案 9 :(得分:0)

jQuery Mobile有一个.tap()事件似乎有你期望的行为:

  

jQuery Mobile点击事件在单个目标对象上发生的快速,完整的触摸事件后触发。它是相当于触摸手势的释放状态触发的标准点击事件的手势。

这可能不一定能回答这个问题,但对某些人来说可能是一个有用的选择。

答案 10 :(得分:0)

如果你想为多个元素执行此操作,还需要鼠标和指针事件:

var elems = $('YOURMULTISELECTOR'); // selector for multiple elements
elems.unbind('mousdown pointerdown touchstart touchmove mouseup pointerup touchend');
var elem = null;
elems.on('mousdown pointerdown touchstart', function (e) {
    elem = yourSingleSelector(e);
}).on('touchmove', function (e) {
    elem = null;                
}).on('mouseup pointerup touchend', function (e) { 
    if (elem == yourSingleSelector(e)) {                    
        // do something
    }
});