简体   繁体   English

滑动时防止触摸启动

[英]prevent touchstart when swiping

I have a scrollable list on a mobile device.我在移动设备上有一个可滚动的列表。 They want people to be able to scroll the list via swiping, and also select a row by tapping.他们希望人们能够通过滑动滚动列表,并且还希望通过点击来滚动一行 select。

The catch is combining the two.问题是将两者结合起来。 I don't want a row to be selected if you are actually scrolling the list.如果您实际上正在滚动列表,我不希望选择一行。 Here's what I've found:这是我发现的:

Doesn't trigger when scrolling:滚动时不触发:

  • click点击
  • mouseup mouseup

Does trigger when scrolling:滚动时触发:

  • mousedown鼠标按下
  • touchstart触摸启动
  • touchend触摸端

The simple solution is to just stick with the click event.简单的解决方案是坚持点击事件。 But what we're finding is that on certain blackberry devices, there is a VERY noticeable lag between touchstart and it then triggering either click or mouseup.但我们发现,在某些 blackberry 设备上,touchstart 与触发 click 或 mouseup 之间存在非常明显的延迟。 This delay is significant enough to make it unusable on those devices.这种延迟足以使其在这些设备上无法使用。

So that leaves us with the other options.所以这给我们留下了其他选择。 However, with those options, you can scroll the list without triggering the row you touched to start the scroll.但是,使用这些选项,您可以滚动列表而无需触发您触摸的行开始滚动。

What is the best practice here to resolve this?解决此问题的最佳做法是什么?

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:我们可以设置一些条件:

  1. Swipe is when you touch at point p1 , then move your finger to point p2 while still having the finger on the screen, then releaseing.滑动是当您触摸点p1时,然后将手指移动到p2点,同时手指仍在屏幕上,然后松开。
  2. 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 .因此,如果您存储touchStart发生位置的坐标,则可以测量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.此外,如果你想做得非常整洁,你还可以检测在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);

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 .然后,您还必须为可滚动元素添加侦听器,但您不必担心如果手指在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. //西蒙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).此外,您仍然可以使用键盘(使用 onclick)。

I think this is similar to Netlight_Digital_Media's answer.我认为这类似于 Netlight_Digital_Media 的回答。 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.:引用 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 Tocca.js: https://github.com/GianlucaGuarini/Tocca.js

It's quite flexible and detects touch as well as swipe, double-tap etc.它非常灵活,可以检测触摸以及滑动、双击等。

I had the same problem, here's a quick solution which works for me我有同样的问题,这是一个适合我的快速解决方案

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

basically instead of handling touchstart immediately, wait for some milliseconds (200ms in this example), then check the scroll position, had scrollposition changed, then we need not to handle touchstart.基本上不是立即处理 touchstart,而是等待几毫秒(本例中为 200 毫秒),然后检查滚动 position,滚动位置已更改,则我们无需处理 touchstart。

I came across this elegant solution that works like a charm using jQuery.我遇到了这个优雅的解决方案,它使用 jQuery 就像一个魅力。 My problem was preventing list items from calling their touch start event during scrolling.我的问题是阻止列表项在滚动期间调用它们的触摸开始事件。 This should also work for swiping.这也应该适用于刷卡。

  1. bind touchstart to each item that will be scrolled or swiped using a class 'listObject'将 touchstart 绑定到将使用 class 'listObject' 滚动或滑动的每个项目

    $('.listObject').live('touchstart', touchScroll);
  2. Then to each item assign a data-object attr defining the function to be called然后为每个项目分配一个数据对象属性,定义要调用的 function

     <button class='listObject' data-object=alert('You are alerted !')>Alert Me</button>

The following function will effectively differentiate between a tap and scrolling or swiping.以下 function 将有效地区分点击和滚动或滑动。

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

}

I came up with this, since i wanted a global event that also can prevent linking dynamically.我想出了这个,因为我想要一个也可以防止动态链接的全局事件。 So preventDefault() must be callable in the event listener, for this the touch event is referenced.因此preventDefault()必须在事件监听器中是可调用的,为此触摸事件被引用。

The detection is like most solutions posted here so far.检测与迄今为止在此处发布的大多数解决方案一样。 On the end of the touch check if the element is still the same or if we moved some amount.在触摸结束时检查元素是否仍然相同或者我们是否移动了一些量。

$('li a').on('ontouchstart' in document.documentElement === true ? 'touchClick' : 'click', handleClick);

if('ontouchstart' in document.documentElement === true) {
    var clickedEl = null;
    var touchClickEvent = $.Event('touchClick');
    var validClick = false;
    var pos = null;
    var moveTolerance = 15;
    $(window).on('touchstart', function(e) {
        /* only handle touch with one finger */
        if(e.changedTouches.length === 1) {
            clickedEl = document.elementFromPoint(e.touches[0].clientX, e.touches[0].clientY);
            pos = {x: e.touches[0].clientX, y: e.touches[0].clientY};
            validClick = true;
        } else {
            validClick = false;
        }
    }).on("touchmove", function(e) {
        var currentEl = document.elementFromPoint(e.touches[0].clientX, e.touches[0].clientY);
        if(
            e.changedTouches.length > 1 ||
            (!$(clickedEl).is(currentEl) && !$.contains(clickedEl, currentEl)) ||
            (Math.abs(pos.y - e.touches[0].clientY) > moveTolerance && Math.abs(pos.x - e.touches[0].clientX) > moveTolerance)
        ) {
            validClick = false;
        }
    }).on("touchend", function(e) {
        if(validClick) {
            /* this allowes calling of preventDefault on the touch chain */
            touchClickEvent.originalEvent = e;
            /* target is used in jQuery event delegation */
            touchClickEvent.target = e.target;
            $(clickedEl).trigger(touchClickEvent);
        }
    });
}

I did this with a bit of a different work around.我做了一些不同的工作。 It's definitely not very elegant and certainly not suited to most situations, but it worked for me.它绝对不是很优雅,当然也不适合大多数情况,但它对我有用。

I have been using jQuery's toggleSlide() to open and close input divs, firing the slide on touchstart.我一直在使用 jQuery 的 toggleSlide() 来打开和关闭输入 div,在 touchstart 上触发幻灯片。 The problem was that when the user wanted to scroll, the touched div would open up.问题是当用户想要滚动时,触摸的 div 会打开。 To stop this from happening, (or to reverse it before the user noticed) I added a touchslide event to the document which would close the last touched div.为了阻止这种情况发生(或在用户注意到之前将其反转),我在文档中添加了一个 touchslide 事件,该事件将关闭最后一次触摸的 div。

In more depth, here is a code snippet:更深入地说,这是一个代码片段:

var lastTouched;

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

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

The global variable stores the last touched div, and if the user swipes, the document.touchmove event hides that div.全局变量存储最后触摸的 div,如果用户滑动,document.touchmove 事件会隐藏该 div。 Sometimes you get a flicker of a div poking out but it works for what I need it to, and is simple enough for me to come up with.有时你会看到一个 div 闪烁,但它可以满足我的需要,并且足够简单,我可以想出。

I use this bit of code so that buttons are only triggered (on touchend) if not being swiped on:我使用这段代码,以便仅在未滑动时触发按钮(在触摸端):

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

jQuery Mobile has a .tap() event which seems to have the behavior you'd expect: jQuery Mobile 有一个.tap()事件,它似乎具有您期望的行为:

The jQuery Mobile tap event triggers after a quick, complete touch event that occurs on a single target object. jQuery 移动点击事件在单个目标 object 上发生快速、完整的触摸事件后触发。 It is the gesture equivalent of a standard click event that is triggered on the release state of the touch gesture.它相当于标准单击事件的手势,在触摸手势的释放 state 上触发。

This might not necessarily answer the question, but might be a useful alternative to some.这可能不一定回答问题,但可能是一些有用的替代方案。

if you want to do this for multiple elements and also need mouse and pointer events:如果您想对多个元素执行此操作并且还需要鼠标和指针事件:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM