在几乎所有当前的浏览器上(广泛的details from patrickhlauke on github,I summarised in an SO answer以及更多信息from QuirksMode),触摸屏触摸都会触发mouseover
事件(有时会创建一个不可见的伪光标,该伪光标一直停留在用户触摸的地方,直到他们触摸其他地方)。
有时,在触摸/点击和鼠标悬停旨在执行不同操作的情况下,这会导致不良行为。
从传递给event
对象的,响应鼠标悬停事件的函数内部,有什么方法可以检查这是否是从元素外部移动到元素内部的移动光标的“真实”鼠标悬停,还是是由触摸屏触摸引起的这种触摸屏行为引起的?event
对象看起来相同。例如,在chrome上,由用户触摸触摸屏引起的鼠标悬停事件具有type: "mouseover"
,我什么都看不到,无法将其标识为与触摸相关。
我的想法是将一个事件绑定(bind)到touchstart
上以更改鼠标悬停事件,然后将一个事件绑定(bind)到touchend
上以删除此更改。不幸的是,这是行不通的,因为事件顺序似乎是touchstart
→touchend
→mouseover
→click
(我不能在不弄乱其他功能的情况下附加normalize-mouseover函数来单击)。
我希望这个问题以前曾被问过,但是现有的问题并没有完全解决它:
我能想到的最好的方法是设置一个触摸事件,该事件设置一些可全局访问的变量标志,例如
window.touchedRecently = true;
上的touchstart
,但不单击,然后在一个500ms的setTimeout
之后删除该标志。虽然这是一个丑陋的hack。注意-我们不能假定触摸屏设备没有像鼠标一样的巡回光标,反之亦然,因为有许多设备使用触摸屏和类似鼠标的笔在悬停在屏幕附近时会移动光标,或者使用触摸屏和触摸屏。鼠标(例如触摸屏笔记本电脑)。我对How do I detect whether a browser supports mouseover events?的回答中有更多详细信息。
注意#2-这不是jQuery的问题,我的事件来自Raphael.js路径,jQuery不允许使用这些路径,并且提供普通的浏览器
event
对象。如果有特定于Raphael的解决方案,我会接受,但是这种可能性很小,并且使用原始JavaScript解决方案会更好。 最佳答案
考虑到问题的复杂性,我认为有必要详细说明任何潜在解决方案中涉及的问题和极端情况。
问题:
1-跨设备和浏览器的触摸事件的不同实现。对某些人有效的方法对其他人绝对无效。您只需要浏览这些patrickhlauke资源,即可了解当前跨设备和浏览器处理触摸屏的过程有何不同。
2-事件处理程序不提供有关其初始触发的线索。 您绝对是正确的,在通过与鼠标的交互调度的鼠标事件与通过触摸交互调度的鼠标事件之间,event
对象是相同的(在大多数情况下,大多数情况下)。
3-涵盖所有设备的此问题的任何解决方案都可能是短暂的,因为当前的W3C建议书并未就如何处理触摸/单击事件(https://www.w3.org/TR/touch-events/)进行详细介绍,因此浏览器将继续具有不同的特点实现。此外,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
期间设置一个标志,并在其中一个click事件期间将其删除。除少数情况外,这将很好地工作:event.preventDefault
在其中一个触摸事件上被调用-由于不会单击事件而不会取消设置该标志,因此该元素上任何将来的真正单击事件仍将被标记为触摸事件不幸的是,这意味着我们将始终需要使用超时。据我所知,既无法确定何时将触摸事件称为
event.preventDefault
,也无法理解何时将触摸元素在DOM中移动以及将点击事件触发到另一个元素上。我认为这是一个引人入胜的场景,因此将很快对此答案进行修改,以包含推荐的代码响应。现在,我建议使用@ibowankenobi提供的答案或@Manuel Otto提供的答案。