在触摸屏上处理鼠标和触摸事件

时间:2013-01-25 21:32:34

标签: javascript google-chrome firefox touch touchscreen

我正在编写应该支持鼠标和触摸交互的Web应用程序。 为了测试,我使用Windows 7的触摸屏设备。我试图在最新的Firefox和Chrome金丝雀中嗅探触摸事件并获得以下结果:

触摸Firefox触发触摸和相应的鼠标事件。 Chrome会触发touchstart/mousedowntouchend/mouseup对,但mousemove会以非常奇怪的方式触发:touchmove时一两次。

所有鼠标事件一如既往地处理。

有没有办法在现代触摸屏上同时处理鼠标和触控平板?如果Firefox触发了一对触摸和鼠标事件,那么Chrome中的touchmove mousemove会发生什么?我应该翻译所有鼠标事件,反之亦然?我希望找到创建响应式界面的正确方法。

5 个答案:

答案 0 :(得分:14)

您无法提前预测要收听哪些事件(例如,您知道在您的页面加载后可能会插入USB触摸屏)。

相反,您应该始终同时监听触摸事件和鼠标事件,但是对您处理的触摸事件调用preventDefault()可以防止(现在多余的)鼠标事件被触发。有关详细信息,请参阅http://www.html5rocks.com/en/mobile/touchandmouse/

答案 1 :(得分:6)

你应该检查触摸界面的可用性并根据它来绑定事件。

您可以这样做:

(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);

<击>


如果你想同时处理这两个并且浏览器没有将触摸事件很好地转换为鼠标事件,你可以捕获触摸事件并阻止它们 - 然后浏览器不应该触发相应的鼠标事件(你不会双重事件)你可以自己发射它作为鼠标事件或只​​是处理它。

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

答案 2 :(得分:1)

MouseEvents和TouchEvents在技术上并不提供完全相同的功能,但在大多数情况下,它们可以互换使用。该解决方案不偏向于另一个,因为用户可能同时具有鼠标和触摸屏。相反,它允许用户使用他们希望的任何输入设备,只要他们在更改输入之前至少等待五秒钟。当点击屏幕时,此解决方案忽略触摸屏设备上的鼠标指针仿真。

&#13;
&#13;
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.
{...}
&#13;
&#13;
&#13;

这绝不是赢得所有解决方案,我已经使用了几次,并且没有发现它的问题,但为了公平我通常只是用它来开始动画时它拍了一个画布,或提供将div转换为按钮的逻辑。我留给大家使用这段代码,找到改进并帮助改进这段代码。(如果找不到更好的解决方案)。

答案 3 :(得分:0)

我一直在使用这个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);

答案 4 :(得分:0)

我发现此线程是因为我有一个类似且更复杂的问题:

  

假设我们创建一个带有js启用滚动区的箭头NEXT / PREVIOUS,我们不仅希望响应触摸和鼠标事件,而且还希望在用户继续按下屏幕或按住鼠标时反复触发它们!

重复事件会使我的下一个按钮前进2个位置,而不是一个位置!

借助闭包,一切似乎都是可能的:

(1)首先创建一个用于隔离变量的自调用函数:

 (function(myScroll, $, window, document, undefined){
 ...
 }(window.myScroll = window.myScroll || {}, jQuery, window, document));

(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)事件锁定功能:

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);
   // similar for 'next*' handlers
}

事件mousedowntouchstart的触发将对同时支持两者的设备上的处理程序产生双重调用(可能首先触发)。 mouseuptouchend也是如此。

我们知道输入设备(实际上是整个图形环境)会顺序产生事件,因此,只要在私有eventLock.next.primary和{{ 1}}用于分别从处理程序eventLock.previous.primarynext*()捕获的第一个事件。

是事件类型,因此第二个,第三个等事件始终是失败者,他们不会借助锁定功能previous*()和{ {1}}。

(5)可以在事件处理程序的定义中看到以上内容:

eventLock()

primaryEventLock()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); }; 类似。

这个想法是,谁先碰触(触摸或鼠标),都不会在nextStart的帮助下获得并在那里停留。

打开此锁的唯一方法是在步骤(4):nextEndfunction eventLock(evt, key)触发终止事件处理程序*End()

我还以一种非常聪明的方式处理了会话中间连接的触摸设备的问题:我注意到连续按下超过previousEnd会连续产生仅回调函数的调用当时的主要事件(原因是没有结束事件处理程序会终止callabck)!

nextEnd

我定义了用户正在使用的设备主要,因此您要做的就是长按,然后{{1 }}里面的闭包!

另外,请注意,执行defaults.pressDelay所花费的时间应小于touchstart event closure closure .... touchend event

(6)最后,让我们将primaryEventLock(evt, 'previous')暴露给全局范围:

previous(event.target)

您应该用眼前的问题fiddle代替对defaults.pressDelay的呼叫。

另外,请注意,在(5b)中,有一个解决问题的解决方案我们如何将参数传递给从init() 调用的函数,即myScroll.init = init; 缺少参数传递机制。