我发现这个帖子,因为我有一个类似且更复杂的问题:
假设我们创建了一个js可启用的可滚动区域,其中包含NEXT/PREVIOUS箭头,我们不仅希望它响应触摸和鼠标事件,而且还希望在用户继续按住屏幕或按住鼠标时重复触发它们!
事件的重复会导致我的下一个按钮前进2个位置而不是一个!
借助闭包的帮助,一切似乎都是可能的:
(1)首先为变量隔离创建自调用函数:
(function(myScroll, $, window, document, undefined){
...
}(window.myScroll = window.myScroll || {}, jQuery, window, document));
(2)然后,添加用于保存setTimeout()
内部状态的私有变量:
var eventLock = {};
var pids = {};
var defaults = {
pressDelay: 100
}
(3)事件锁定功能:
function getEventLock(evt, key){
if(typeof(eventLock[key]) == 'undefined'){
eventLock[key] = {};
eventLock[key].primary = evt.type;
return true;
}
if(evt.type == eventLock[key].primary)
return true;
else
return false;
}
function primaryEventLock(evt, key){
eventLock[key].primary = evt.type;
}
(4) 添加事件处理程序:
function init(){
$('sth').off('mousedown touchstart', previousStart).on('mousedown touchstart', previousStart);
$('sth').off('mouseup touchend', previousEnd).on('mouseup touchend', previousEnd);
}
触发事件mousedown
和touchstart
将为支持两者的设备上的处理程序产生双重调用(可能是触摸先触发)。mouseup
和touchend
也是如此。
我们知道输入设备(整个图形环境实际上)按顺序产生事件,因此我们不关心哪个首先触发,只要在从处理程序next*()
和previous*()
捕获的第一个事件中设置了特殊键,即私有eventLock.next.primary
和eventLock.previous.primary
。
该键是事件类型,因此第二、第三等事件始终是失败者,它们不能通过锁定函数eventLock()
和primaryEventLock()
获取锁。
(5)上述内容可以在事件处理程序的定义中看到:
function previousStart(evt){
if(!getEventLock(evt, 'previous'))
return;
previous(evt.target);
pids.previous = setTimeout(closure, defaults.pressDelay);
function closure(){
previous(evt.target);
primaryEventLock(evt, 'previous');
pids.previous = setTimeout(closure, defaults.pressDelay);
}
};
function previousEnd(evt){
clearTimeout(pids.previous);
};
nextStart
和nextEnd
同理。
思路是,无论是触摸还是鼠标,第一个之后的人都不能通过function eventLock(evt, key)
获得锁定并停在那里。
唯一打开此锁定的方法是在步骤(4)中触发终止事件处理程序*End()
: previousEnd
和nextEnd
。
我还以非常聪明的方式处理了在会话中间连接的触摸设备的问题:我注意到长按时间超过defaults.pressDelay
会产生连续的回调函数调用,仅针对该时刻的主要事件(原因是没有结束事件处理程序终止回调)!
touchstart event
closure
closure
....
touchend event
我将“主要设备”定义为用户正在使用的设备,因此您只需长按即可使用primaryEventLock(evt,'previous')
在闭包内立即使您的设备成为主要设备!
另外,请注意执行previous(event.target)
所需的时间应小于defaults.pressDelay
。
(6) 最后,让我们将init()
暴露给全局范围:
myScroll.init = init
你应该用手头的问题
fiddle 替换对
previous(event.target)
的调用。
同时,注意到在
(5b) 中有另一个常见问题的解决方案:
我们如何将参数传递给从 setTimeout()
调用的函数,即
setTimeout(previous, defaults.pressDelay)
缺乏参数传递机制。
Player.js:52 [Intervention] Unable to preventDefault inside passive event listener due to target being treated as passive. See https://www.chromestatus.com/features/5093566007214080
。 - David Callanan