use*_*ca8 26 javascript cross-browser touch raphael
几乎所有当前的浏览器(来自github上的patrickhlauke的详细信息,我在SO答案中总结,以及来自QuirksMode的更多信息),触摸屏触摸触发mouseover
事件(有时创建一个隐藏的伪光标,保持用户触摸的位置直到它们触摸其他地方).
有时,在触摸/点击和鼠标悬停用于执行不同操作的情况下,这会导致不良行为.
从响应鼠标悬停事件的函数内部,已经传递了该event
对象,有什么方法可以检查这是否是从一个元素外部移动到其内部的移动光标的"真正"鼠标悬停,或者它是否是由触摸屏触摸触摸屏行为引起的?
该event
对象看起来相同.例如,在Chrome上,由用户触摸触摸屏引起的鼠标悬停事件已经type: "mouseover"
没有任何我可以看到的将其识别为触摸相关的事件.
我有想法将事件绑定到touchstart
更改鼠标悬停事件,然后事件将touchend
删除此更改.不幸的是,这是行不通的,因为事件顺序似乎touchstart
→交通touchend
→交通mouseover
→交通click
(我不能附加正规化,鼠标悬停功能,点击不搞乱其他功能).
我以前曾经问过这个问题,但是现有的问题并没有完全消除它:
我能想到的最好的是有一个设置一些全局访问的变量标志一样,也就是说,一个触摸事件window.touchedRecently = true;
上touchstart
,但不能点击,再经过消除了这个标志,也就是说,一个500毫秒setTimeout
.这是一个丑陋的黑客.
注意 - 我们不能假设触摸屏设备没有类似鼠标的漫游光标,反之亦然,因为有许多设备使用触摸屏和类似鼠标的笔,当在屏幕附近悬停时移动光标,或使用触摸屏和鼠标(例如触摸屏笔记本电脑).我的答案中的更多细节如何检测浏览器是否支持鼠标悬停事件?.
注意#2 - 这不是一个jQuery问题,我的事件来自Raphael.js路径,jQuery不是一个选项,它提供了一个简单的vanilla浏览器event
对象.如果有一个Raphael特定的解决方案,我会接受,但这是不太可能的,原始的JavaScript解决方案会更好.
鉴于问题的复杂性,我认为值得详细说明任何潜在解决方案中涉及的问题和边缘情况.
问题:
1 - 跨设备和浏览器的触摸事件的不同实现.对某些人有用的东西绝对不适用于其他人.您只需要浏览一下 patrickhlauke资源,就可以了解当前在设备和浏览器之间处理触摸屏的过程有多么不同.
2 - 事件处理程序不提供其初始触发器的线索.你也绝对正确地说,event
通过与鼠标的交互调度的鼠标事件与通过触摸交互调度的鼠标事件之间的对象是相同的(当然在绝大多数情况下).
3 - 涵盖所有设备的这个问题的任何解决方案很可能是短暂的,因为当前的W3C建议书没有详细说明如何处理触摸/点击事件( https://www.w3.org/TR/ touch-events /),因此浏览器将继续具有不同的实现.触摸事件标准文档似乎在过去5年中没有变化,因此这不会很快解决.https://www.w3.org/standards/history/touch-events
4 - 理想情况下,解决方案不应该使用超时,因为没有从触摸事件到鼠标事件的定义时间,并且根据规范,很可能不会很快.不幸的是,超时几乎是不可避免的,我稍后会解释.
未来的解决方案:
在将来,解决方案可能是使用 Pointer Events
而不是鼠标/触摸事件,因为这些事件给我们pointerType
( https://developer.mozilla.org/en-US/docs/Web/API/Pointer_events),但不幸的是我们'目前尚未建立标准,因此跨浏览器兼容性( https://caniuse.com/#search=pointer%20events)很差.
我们现在如何解决这个问题
如果我们接受:
然后我们只能使用有关鼠标事件本身的数据来确定其来源.正如我们已经建立的那样,浏览器不提供这个,所以我们需要自己添加它.执行此操作的唯一方法是使用与鼠标事件同时触发的触摸事件.
再看看patrickhlauke资源,我们可以发表一些声明:
mouseover
总是跟着单击事件mousedown
mouseup
和click
-总的顺序.(有时被其他事件分开).这得到了W3C建议的支持:https://www.w3.org/TR/touch-events/.mouseover
事件始终在pointerover
其MS对应部分MSPointerOver
或之前touchstart
mouseover
必须忽略事件顺序开头的设备/浏览器.在触发触摸事件本身之前,我们无法确定触摸事件是否触发了鼠标事件.鉴于此,我们可以在设置一个标志pointerover
,MSPointerOver
和touchstart
,然后单击事件之一时将其删除.这样做很有效,除了一些案例:
event.preventDefault
在其中一个触摸事件上调用 - 该标志永远不会被设置,因为不会调用点击事件,因此此元素上任何未来的真正点击事件仍将被标记为触摸事件如果在处理触摸事件期间文档的内容已经改变,则用户代理可以将鼠标事件分派给与触摸事件不同的目标.
不幸的是,这意味着我们总是需要使用超时.据我所知,无法确定触摸事件何时调用event.preventDefault
,也无法理解触摸元素何时在DOM中移动以及触发另一个元素上的点击事件.
我认为这是一个非常有趣的场景,所以这个答案将很快修改,以包含推荐的代码响应.现在,我会推荐@ibowankenobi提供的答案或@Manuel Otto提供的答案.
根据https://www.html5rocks.com/en/mobile/touchandmouse/
对于单击事件的顺序是:
因此,您可以isFromTouchEvent = true;
在 onTouchStart() 和isFromTouchEvent = false;
onClick() 中设置一些任意布尔值,并检查 onMouseOver() 内部的值。这不能很好地工作,因为我们不能保证在我们尝试侦听的元素中获得所有这些事件。
我们所知道的是:
当用户不使用鼠标时
mouseover
直接(内800毫秒)或者是后烧制是touchend
或
touchstart
(如果用户敲击和保持).mouseover
和touchstart
/ touchend
是相同的.当用户使用鼠标/笔时
mouseover
触摸事件之前被解雇,即使没有,的位置mouseover
将不匹配的触摸事件的立场时间99%.牢记这些要点,我制作了一个片段,triggeredByTouch = true
如果符合列出的条件,它将为事件添加一个标志.此外,您可以将此行为添加到其他鼠标事件或设置kill = true
,以便完全丢弃触摸触发的鼠标事件.
(function (target){
var keep_ms = 1000 // how long to keep the touchevents
var kill = false // wether to kill any mouse events triggered by touch
var touchpoints = []
function registerTouch(e){
var touch = e.touches[0] || e.changedTouches[0]
var point = {x:touch.pageX,y:touch.pageY}
touchpoints.push(point)
setTimeout(function (){
// remove touchpoint from list after keep_ms
touchpoints.splice(touchpoints.indexOf(point),1)
},keep_ms)
}
function handleMouseEvent(e){
for(var i in touchpoints){
//check if mouseevent's position is (almost) identical to any previously registered touch events' positions
if(Math.abs(touchpoints[i].x-e.pageX)<2 && Math.abs(touchpoints[i].y-e.pageY)<2){
//set flag on event
e.triggeredByTouch = true
//if wanted, kill the event
if(kill){
e.cancel = true
e.returnValue = false
e.cancelBubble = true
e.preventDefault()
e.stopPropagation()
}
return
}
}
}
target.addEventListener('touchstart',registerTouch,true)
target.addEventListener('touchend',registerTouch,true)
// which mouse events to monitor
target.addEventListener('mouseover',handleMouseEvent,true)
//target.addEventListener('click',handleMouseEvent,true) - uncomment or add others if wanted
})(document)
Run Code Online (Sandbox Code Playgroud)
试试看:
function onMouseOver(e){
console.log('triggered by touch:',e.triggeredByTouch ? 'yes' : 'no')
}
(function (target){
var keep_ms = 1000 // how long to keep the touchevents
var kill = false // wether to kill any mouse events triggered by touch
var touchpoints = []
function registerTouch(e){
var touch = e.touches[0] || e.changedTouches[0]
var point = {x:touch.pageX,y:touch.pageY}
touchpoints.push(point)
setTimeout(function (){
// remove touchpoint from list after keep_ms
touchpoints.splice(touchpoints.indexOf(point),1)
},keep_ms)
}
function handleMouseEvent(e){
for(var i in touchpoints){
//check if mouseevent's position is (almost) identical to any previously registered touch events' positions
if(Math.abs(touchpoints[i].x-e.pageX)<2 && Math.abs(touchpoints[i].y-e.pageY)<2){
//set flag on event
e.triggeredByTouch = true
//if wanted, kill the event
if(kill){
e.cancel = true
e.returnValue = false
e.cancelBubble = true
e.preventDefault()
e.stopPropagation()
}
return
}
}
}
target.addEventListener('touchstart',registerTouch,true)
target.addEventListener('touchend',registerTouch,true)
// which mouse events to monitor
target.addEventListener('mouseover',handleMouseEvent,true)
//target.addEventListener('click',handleMouseEvent,true) - uncomment or add others if wanted
})(document)
Run Code Online (Sandbox Code Playgroud)
a{
font-family: Helvatica, Arial;
font-size: 21pt;
}
Run Code Online (Sandbox Code Playgroud)
<a href="#" onmouseover="onMouseOver(event)">Click me</a>
Run Code Online (Sandbox Code Playgroud)
您可以使用 Modernizr 来实现这一点!我刚刚在本地开发服务器上测试了它并且它有效。
if (Modernizr.touch) {
console.log('Touch Screen');
} else {
console.log('No Touch Screen');
}
Run Code Online (Sandbox Code Playgroud)
那么我会从那里开始吗?
归档时间: |
|
查看次数: |
5316 次 |
最近记录: |