Javascript 处理触摸屏上的鼠标和触摸事件
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/14530734/
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
handle both mouse and touch events on touch screens
提问by johnny
I'm writing web application which should support both mouse and touch interactions. For testing I use touch screen device with Windows 7. I've tried to sniff touch events in latest Firefox and Chrome canary and got the following results:
我正在编写应该支持鼠标和触摸交互的 Web 应用程序。为了测试,我使用 Windows 7 的触摸屏设备。我尝试在最新的 Firefox 和 Chrome canary 中嗅探触摸事件,并得到以下结果:
On touch Firefox fires touch and corresponding mouse event.
Chrome fires touchstart/mousedown, touchend/mouseuppairs, but mousemovefired in very strange manner: one/two times while touchmove.
在触摸 Firefox 触发触摸和相应的鼠标事件。Chrome 会触发touchstart/mousedown,touchend/mouseup成对,但mousemove以非常奇怪的方式触发:一次/两次 while touchmove。
All mouse events handled as always.
像往常一样处理所有鼠标事件。
Is there any way to handle mouse and touch evens simultaneously on modern touch screens? If Firefox fires a pair of touch and mouse event what happens on touchmovewith mousemovein Chrome? Should I translate all mouse events to touch or vice versa? I hope to find right way to create responsive interface.
有没有办法在现代触摸屏上同时处理鼠标和触摸事件?如果Firefox触发一个对触摸和鼠标事件的发生的事情上touchmove与mousemoveChrome浏览器?我应该将所有鼠标事件转换为触摸,反之亦然?我希望找到正确的方法来创建响应式界面。
采纳答案by lupatus
You should rather check availability of touch interface and bind events according to that.
您应该检查触摸界面的可用性并据此绑定事件。
You can do something like this:
(function () {
if ('ontouchstart' in window) {
window.Evt = {
PUSH : 'touchstart',
MOVE : 'touchmove',
RELEASE : 'touchend'
};
} else {
window.Evt = {
PUSH : 'mousedown',
MOVE : 'mousemove',
RELEASE : 'mouseup'
};
}
}());
// and then...
document.getElementById('mydiv').addEventListener(Evt.PUSH, myStartDragHandler, false);
你可以这样做:
(function () {
if ('ontouchstart' in window) {
window.Evt = {
PUSH : 'touchstart',
MOVE : 'touchmove',
RELEASE : 'touchend'
};
} else {
window.Evt = {
PUSH : 'mousedown',
MOVE : 'mousemove',
RELEASE : 'mouseup'
};
}
}());
// and then...
document.getElementById('mydiv').addEventListener(Evt.PUSH, myStartDragHandler, false);
If you want to handle both in same time and browser does not translate well touch events into mouse events, you can catch touch events and stop them - then corresponding mouse event shouldn't be fired by browser (you won't have double events) and you can fire it yourself as mouse event or just handle it.
如果您想同时处理两者并且浏览器不能很好地将触摸事件转换为鼠标事件,您可以捕获触摸事件并停止它们 - 那么浏览器不应触发相应的鼠标事件(您不会有双重事件)您可以将其作为鼠标事件自行触发或仅处理它。
var mydiv = document.getElementsById('mydiv');
mydiv.addEventListener('mousemove', myMoveHandler, false);
mydiv.addEventListener('touchmove', function (e) {
// stop touch event
e.stopPropagation();
e.preventDefault();
// translate to mouse event
var clkEvt = document.createEvent('MouseEvent');
clkEvt.initMouseEvent('mousemove', true, true, window, e.detail,
e.touches[0].screenX, e.touches[0].screenY,
e.touches[0].clientX, e.touches[0].clientY,
false, false, false, false,
0, null);
mydiv.dispatchEvent(clkEvt);
// or just handle touch event
myMoveHandler(e);
}, false);
回答by Rick Byers
You can't really predict in advance which events to listen for (eg. for all you know a USB touch screen could get plugged in after your page has loaded).
您无法真正提前预测要侦听哪些事件(例如,就您所知,在您的页面加载后,USB 触摸屏可能会被插入)。
Instead, you should always listen to both the touch events and mouse events, but call preventDefault() on the touch events you handle to prevent (now redundant) mouse events from being fired for them. See http://www.html5rocks.com/en/mobile/touchandmouse/for details.
相反,您应该始终同时侦听触摸事件和鼠标事件,但在处理的触摸事件上调用 preventDefault() 以防止(现在是多余的)鼠标事件为它们触发。有关详细信息,请参阅http://www.html5rocks.com/en/mobile/touchandmouse/。
回答by FutureSci
MouseEvents and TouchEvents do not technically provide exactly the same functionality, but for most purposes , they can be used interchangeably. This solution does not favor one over the other, as the user may have both a mouse and a touch screen. Instead, it allows the user to use which ever input device they wish, as long as they wait at least five seconds before changing inputs. This solution ignores mouse pointer emulation on touchscreen devices when the screen is tapped.
MouseEvents 和 TouchEvents 在技术上并不提供完全相同的功能,但在大多数情况下,它们可以互换使用。这种解决方案并不偏袒另一个,因为用户可能同时拥有鼠标和触摸屏。相反,它允许用户使用他们想要的任何输入设备,只要他们在更改输入之前等待至少五秒钟。当点击屏幕时,此解决方案会忽略触摸屏设备上的鼠标指针模拟。
var lastEvent = 3 ;
var MOUSE_EVENT = 1;
var TOUCH_EVENT = 2 ;
element.addEventListener('touchstart', function(event)
{
if (lastEvent === MOUSE_EVENT )
{
var time = Date.now() - eventTime ;
if ( time > 5000 )
{
eventTime = Date.now() ;
lastEvent = TOUCH_EVENT ;
interactionStart(event) ;
}
}
else
{
lastEvent = TOUCH_EVENT ; ;
eventTime = Date.now() ;
interactionStart(event) ;
}
}) ;
element.addEventListener('mousedown', function(event)
{
if (lastEvent === TOUCH_EVENT )
{
var time = Date.now() - eventTime ;
if ( time > 5000 )
{
eventTime = Date.now() ;
lastEvent = MOUSE_EVENT ;
interactionStart(event) ;
}
}
else
{
lastEvent= MOUSE_EVENT ;
eventTime = Date.now() ;
interactionStart(event) ;
}
}) ;
function interactionStart(event) // handle interaction (touch or click ) here.
{...}
This is by no means a win all solution, I have used this a few times , and have not found problems with it, but to be fair i usually just use it to start animation when a canvas it tapped , or to provide logic to turn a div into a button. I leave it to you all to use this code , find improvements and help to improve this code.(If you do not find a better solution ).
这绝不是一个双赢的解决方案,我已经使用了几次,并没有发现它的问题,但公平地说,我通常只是在它点击画布时使用它来启动动画,或者提供逻辑来转动一个div变成一个按钮。我将使用此代码的工作留给大家,寻找改进并帮助改进此代码。(如果您没有找到更好的解决方案)。
回答by centurian
I found this thread because I have a similar & more complex problem:
我发现这个线程是因为我有一个类似且更复杂的问题:
supposing we create a js enabled scrollable area with arrows NEXT/PREVIOUS which we want not only to respond to touch and mouse events but also to fire them repeatedly while the user continues to press the screen or hold down his/her mouse!
假设我们创建了一个带有箭头 NEXT/PREVIOUS 的启用 js 的可滚动区域,我们不仅要响应触摸和鼠标事件,还要在用户继续按下屏幕或按住他/她的鼠标时重复触发它们!
Repetition of events would make my next button to advance 2 positions instead one!
重复事件会使我的下一个按钮前进 2 个位置而不是一个!
With the help of closures everything seems possible:
在闭包的帮助下,一切似乎都是可能的:
(1) First create a self invoking function for variable isolation:
(1)首先创建一个自调用函数进行变量隔离:
(function(myScroll, $, window, document, undefined){
...
}(window.myScroll = window.myScroll || {}, jQuery, window, document));
(2) Then, add your private variables that will hold internal state from setTimeout():
(2) 然后,添加将保持内部状态的私有变量setTimeout():
/*
* Primary events for handlers that respond to more than one event and devices
* that produce more than one, like touch devices.
* The first event in browser's queue hinders all subsequent for the specific
* key intended to be used by a handler.
* Every key points to an object '{primary: <event type>}'.
*/
var eventLock = {};
// Process ids based on keys.
var pids = {};
// Some defaults
var defaults = {
pressDelay: 100 // ms between successive calls for continuous press by mouse or touch
}
(3) The event lock functions:
(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) Attach your event handlers:
(4) 附加您的事件处理程序:
function init(){
$('sth').off('mousedown touchstart', previousStart).on('mousedown touchstart', previousStart);
$('sth').off('mouseup touchend', previousEnd).on('mouseup touchend', previousEnd);
// similar for 'next*' handlers
}
Firing of events mousedownand touchstartwill produce double calls for handlers on devices that support both (probably touch fires first). The same applies to mouseupand touchend.
触发事件mousedown并将touchstart在支持两者的设备上产生对处理程序的双重调用(可能首先触发触摸)。这同样适用于mouseup和touchend。
We know that input devices (whole graphic environments actually) produce events sequentially so we don't care which fires first as long a special keyis set at private eventLock.next.primaryand eventLock.previous.primaryfor the first events captured from handlers next*()and previous*()respectively.
我们知道,输入设备(整个图形环境实际上)产生的事件顺序,所以我们不关心哪个先发生,只要一个特殊键被设置在私人eventLock.next.primary和eventLock.previous.primary从处理程序捕获的第一事件next*()和previous*()分别。
That keyis the event type so that the second, third etc. event are always losers, they don't acquire the lock with the help of the lock functions eventLock()and primaryEventLock().
该键是事件类型,因此第二个、第三个等事件总是失败者,它们不会在锁函数eventLock()和的帮助下获取锁primaryEventLock()。
(5) The above can be seen at the definition of the event handlers:
(5) 以上可以在事件处理程序的定义中看到:
function previousStart(evt){
// 'race' condition/repetition between 'mousedown' and 'touchstart'
if(!getEventLock(evt, 'previous'))
return;
// a. !!!you have to implement this!!!
previous(evt.target);
// b. emulate successive events of this type
pids.previous = setTimeout(closure, defaults.pressDelay);
// internal function repeats steps (a), (b)
function closure(){
previous(evt.target);
primaryEventLock(evt, 'previous');
pids.previous = setTimeout(closure, defaults.pressDelay);
}
};
function previousEnd(evt){
clearTimeout(pids.previous);
};
Similar for nextStartand nextEnd.
类似的nextStart和nextEnd。
The idea is that whoever comes after the first (touch or mouse) does not acquire a lockwith the help of function eventLock(evt, key)and stops there.
这个想法是,在第一个(触摸或鼠标)之后的任何人都不会在帮助下获得锁定并在function eventLock(evt, key)那里停下来。
The only way to open this lock is to fire the terminationevent handlers *End()at step (4): previousEndand nextEnd.
打开此锁的唯一方法是在步骤 (4) 中触发终止事件处理程序*End():previousEnd和nextEnd。
I also handle the problem of touch devices attached in the middle of the session with a very smart way: I noticed that a continuous press longer than defaults.pressDelayproduces successive calls of the callback function onlyfor the primary event at that time (the reason is that no end event handler terminates the callabck)!
我还以一种非常聪明的方式处理了在会话中间连接的触摸设备的问题:我注意到连续按下的时间比defaults.pressDelay当时仅针对主要事件产生回调函数的连续调用时间长(原因是没有结束事件处理程序终止 callabck)!
touchstart event
closure
closure
....
touchend event
I define primarythe device the user is using so, all you have to do is just press longer and immediately your device becomes primarywith the help of primaryEventLock(evt, 'previous')inside the closure!
我定义了用户正在使用的设备的主要设备,因此您只需按更长的时间,您的设备就会立即在封盖内部的帮助下成为主要设备primaryEventLock(evt, 'previous')!
Also, note that the time it takes to execute previous(event.target)should be smaller than defaults.pressDelay.
另请注意,执行所需的时间previous(event.target)应小于defaults.pressDelay.
(6) Finally, let's expose init()to the global scope:
(6) 最后,让我们暴露init()给全局范围:
myScroll.init = init;
You should replace the call to previous(event.target)with the problem at hand: fiddle.
您应该将调用替换previous(event.target)为手头的问题:fiddle。
Also, note that at (5b)there is a solution to another popular question how do we pass arguments to a function called from setTimeout(), i.e. setTimeout(previous, defaults.pressDelay)lacks an argument passing mechanism.
另外,请注意,在(5b) 处有另一个流行问题的解决方案,我们如何将参数传递给调用 from 的函数setTimeout(),即setTimeout(previous, defaults.pressDelay)缺少参数传递机制。
回答by Joshua
I have been using this jQuery helper to bind both touch and click events.
我一直在使用这个 jQuery 助手来绑定触摸和点击事件。
(function ($) {
$.fn.tclick = function (onclick) {
this.bind("touchstart", function (e) { onclick.call(this, e); e.stopPropagation(); e.preventDefault(); });
this.bind("click", function (e) { onclick.call(this, e); }); //substitute mousedown event for exact same result as touchstart
return this;
};
})(jQuery);

