Javascript 滑动时防止touchstart

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7069458/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 00:42:44  来源:igfitidea点击:

prevent touchstart when swiping

javascriptjqueryscrolltouchtouch-event

提问by DA.

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.

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

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
  • 点击
  • 鼠标向上

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. This delay is significant enough to make it unusable on those devices.

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

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?

解决此问题的最佳实践是什么?

回答by Levarne Sobotker

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

回答by Netlight_Digital_Media

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 p2while still having the finger on the screen, then releaseing.
  2. A click is when you tap start tapping and end tapping on the same element.
  1. 轻扫是指您触摸点p1,然后将手指移动到点,p2同时手指仍留在屏幕上,然后松开。
  2. 单击是指在同一元素上点击开始点击和结束点击。

So, if you store the coordinates of where your touchStartoccured, you can measure the difference at touchEnd. If the change is large enough, consider it a swipe, otherwise, consider it a click.

因此,如果您存储touchStart发生地点的坐标,则可以在 处测量差异touchEnd。如果变化足够大,则认为是滑动,否则,认为是点击。

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 clickCancelmethod which removes highlights etc.

此外,如果您想做得非常整洁,您还可以检测在 a 期间用手指“悬停”在哪个元素上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 touchStartand touchEnd.

然后,您还必须为可滚动元素添加侦听器,但是您不必担心如果手指移动到touchStart和 之间会发生什么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.

// 西蒙 A.

回答by DA.

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. I need to study that one a bit more.

我认为这类似于 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()
}
});

回答by josualeonard

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

回答by AC Patrice

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.

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

回答by Mahes

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 毫秒),然后检查滚动位置,如果滚动位置发生变化,那么我们就不需要处理 touchstart。

回答by alQemist

I came across this elegant solution that works like a charm using jQuery. My problem was preventing list items from calling their touch start event during scrolling. This should also work for swiping.

我遇到了这个优雅的解决方案,它使用 jQuery 就像一个魅力。我的问题是阻止列表项在滚动期间调用它们的触摸开始事件。这也应该适用于刷卡。

  1. bind touchstart to each item that will be scrolled or swiped using a class 'listObject'

    $('.listObject').live('touchstart', touchScroll);
    
  2. Then to each item assign a data-object attr defining the function to be called

    <button class='listObject' data-object=alert('You are alerted !')>Alert Me</button>
    
  1. 使用类“listObject”将 touchstart 绑定到将滚动或滑动的每个项目

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

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

}

回答by LocDog

I use this bit of code so that buttons are only triggered (on touchend) if not being swiped on:

我使用了这段代码,以便只有在没有被滑动时才会触发按钮(在 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")
    }
});

回答by Wex

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. It is the gesture equivalent of a standard click event that is triggered on the release state of the touch gesture.

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

This might not necessarily answer the question, but might be a useful alternative to some.

这可能不一定回答问题,但可能是一些有用的替代方案。

回答by Abraham Brookes

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. The problem was that when the user wanted to scroll, the touched div would open up. 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.

我一直在使用 jQuery 的 toggleSlide() 打开和关闭输入 div,在 touchstart 上触发幻灯片。问题是当用户想要滚动时,触摸的 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. 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,如果用户滑动,document.touchmove 事件会隐藏该 div。有时你会看到一个 div 的闪烁,但它适用于我需要的东西,而且很简单让我想出来。