滑动时防止touchstart

IT技术 javascript jquery scroll touch touch-event
2021-02-22 14:00:53

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

问题是将两者结合起来。如果您实际上是在滚动列表,我不希望选择一行。这是我发现的:

滚动时不触发:

  • 点击
  • 鼠标向上

滚动时触发:

  • 鼠标按下
  • 触摸启动
  • 触端

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

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

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

6个回答
var touchmoved;
$('button').on('touchend', function(e){
    if(touchmoved != true){
        // button click action
    }
}).on('touchmove', function(e){
    touchmoved = true;
}).on('touchstart', function(){
    touchmoved = false;
});
好的!事件的顺序 - 或者更好地说 touchmoved 变量的分配非常重要。如果你切换它们就行不通(至少在 vanilla JS 中不行)
2021-05-04 14:00:53
这样做会以某种方式取消我的滚动操作。
2021-05-06 14:00:53
谢谢你。我是 5 hors 试图找到这个解决方案!
2021-05-21 14:00:53

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

我们可以设定一些条件:

  1. 轻扫是指您触摸点p1,然后将手指移动到点,p2同时手指仍留在屏幕上,然后松开。
  2. 单击是指您在同一元素上点击开始点击和结束点击。

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

此外,如果您想做得非常整洁,您还可以检测在 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);

然后,您还必须为可滚动元素添加侦听器,但是您不必担心如果手指移动到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);

// 西蒙 A.

这是我最终想出的允许通过滑动滚动项目列表,但也允许通过点击“触发”每个项目。此外,您仍然可以使用键盘(使用 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()
}
});
我在我的项目中测试了这种方法。唯一的问题是,如果用户向上滚动,然后向下滚动(几乎到第一个位置),它被假定为点击。
2021-04-22 14:00:53
我在这里看到的另一个问题是 .live() 的使用,它在 jQuery v1.7 中已被弃用并在 1.9 中删除。改用 .on() 。
2021-04-25 14:00:53
lolwut:你必须获取你行,并将其分配给变量$clickableItem,否则clickableItemundefined当你调用addClass就可以了。像这样:var clickableItem = $("#row")[0];
2021-05-06 14:00:53
嗯,TypeError: 'undefined' is not an object当我点击一行时,我的 iPhone 记录了一个错误我替换whateverFunctionYouWantToTriggerOnTapOrClick()alert('tapped'),但没有被调用。
2021-05-09 14:00:53
是的,那不是工作代码……您需要根据需要放入自己的逻辑。仅是显示所使用逻辑的示例。
2021-05-16 14:00:53

引用 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");
});
这对我来说非常有效,对我的用例进行了一些修改。非常感激!
2021-04-21 14:00:53

其中一些解决方案对我有用,但最终我发现这个轻量级库更易于设置。

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

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

同意。我有许多元素必须在移动设备中点击两次而不是一次,所以我用“点击”事件替换了所有“点击”事件,现在桌面和移动设备的工作方式相同。:)
2021-05-11 14:00:53