[英]prevent touchstart when swiping
我在移动设备上有一个可滚动的列表。 他们希望人们能够通过滑动滚动列表,并且还希望通过点击来滚动一行 select。
问题是将两者结合起来。 如果您实际上正在滚动列表,我不希望选择一行。 这是我发现的:
滚动时不触发:
滚动时触发:
简单的解决方案是坚持点击事件。 但我们发现,在某些 blackberry 设备上,touchstart 与触发 click 或 mouseup 之间存在非常明显的延迟。 这种延迟足以使其在这些设备上无法使用。
所以这给我们留下了其他选择。 但是,使用这些选项,您可以滚动列表而无需触发您触摸的行开始滚动。
解决此问题的最佳做法是什么?
var touchmoved;
$('button').on('touchend', function(e){
if(touchmoved != true){
// button click action
}
}).on('touchmove', function(e){
touchmoved = true;
}).on('touchstart', function(){
touchmoved = false;
});
您基本上想要做的是检测什么是滑动,什么是点击。
我们可以设置一些条件:
p1
时,然后将手指移动到p2
点,同时手指仍在屏幕上,然后松开。 因此,如果您存储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);
然后,您还必须为可滚动元素添加侦听器,但您不必担心如果手指在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()
}
});
引用 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");
});
其中一些解决方案对我有用,但最后我发现这个轻量级库更易于设置。
Tocca.js: https://github.com/GianlucaGuarini/Tocca.js
它非常灵活,可以检测触摸以及滑动、双击等。
我有同样的问题,这是一个适合我的快速解决方案
$(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,而是等待几毫秒(本例中为 200 毫秒),然后检查滚动 position,滚动位置已更改,则我们无需处理 touchstart。
我遇到了这个优雅的解决方案,它使用 jQuery 就像一个魅力。 我的问题是阻止列表项在滚动期间调用它们的触摸开始事件。 这也应该适用于刷卡。
将 touchstart 绑定到将使用 class 'listObject' 滚动或滑动的每个项目
$('.listObject').live('touchstart', touchScroll);
然后为每个项目分配一个数据对象属性,定义要调用的 function
<button class='listObject' data-object=alert('You are alerted !')>Alert Me</button>
以下 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")
});
}
我想出了这个,因为我想要一个也可以防止动态链接的全局事件。 因此preventDefault()
必须在事件监听器中是可调用的,为此触摸事件被引用。
检测与迄今为止在此处发布的大多数解决方案一样。 在触摸结束时检查元素是否仍然相同或者我们是否移动了一些量。
$('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);
}
});
}
我做了一些不同的工作。 它绝对不是很优雅,当然也不适合大多数情况,但它对我有用。
我一直在使用 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。 有时你会看到一个 div 闪烁,但它可以满足我的需要,并且足够简单,我可以想出。
我使用这段代码,以便仅在未滑动时触发按钮(在触摸端):
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 有一个.tap()
事件,它似乎具有您期望的行为:
jQuery 移动点击事件在单个目标 object 上发生快速、完整的触摸事件后触发。 它相当于标准单击事件的手势,在触摸手势的释放 state 上触发。
这可能不一定回答问题,但可能是一些有用的替代方案。
如果您想对多个元素执行此操作并且还需要鼠标和指针事件:
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.