理解触摸事件

81

我正在尝试让我的一些库适用于触摸设备,但是我很难弄清它们的支持方式和工作原理。

基本上,有5个触摸事件,但似乎移动浏览器只在touchstart事件上达成了共识(当然)。我创建了一个代码片段作为测试案例。

我已经在我的带有Android 4的Galaxy Note上测试过了,但你也可以在桌面浏览器中查看该链接。

目标是尝试找出如何处理轻敲、双击和长按。没有什么花哨的东西。

基本上,这就是发生的事情:

Android原生浏览器不会触发触摸事件,它只会尝试通过轻敲来模拟鼠标点击,连续触发mousedownmouseupclick事件,但双击只会放大和缩小页面。

Chrome for Android在手指接触屏幕时触发touchstart事件。如果它很快被释放,则触发mousedownmouseuptouchend和最后是click事件。

长按的情况下,大约半秒后触发mousedownmouseup,并在抬起手指时触发touchend,没有最后的click事件。

如果你移动手指,它会触发一次或两次touchmove事件,然后触发一个touchcancel事件,之后什么都不会发生,甚至当你抬起手指时也不会触发touchend事件。

一个双击会触发缩放功能,但在事件上会触发touchstarttouchevent组合两次,没有鼠标事件被触发。
对于Android版的Firefox,它会正确地触发touchstart事件,并且在短时间内触发mousedownmouseuptouchendclick事件。
对于长按的情况,它会触发mousedownmouseup和最后的touchend事件。对于这些内容,Chrome的表现也是一样的。
但如果您移动手指,它会持续触发touchmove(如您所预期),但当手指离开带有事件侦听器的元素时,它不会触发touchleave事件,并且当手指离开浏览器视口时不会触发touchcancel事件。
对于双击,它的行为就像Chrome一样。 Opera Mobile在短时间内做了和Chrome和Firefox一样的事情,但是在长按的情况下会激活某种共享功能,我真的想要禁用它。如果您移动手指或双击,它的行为就像Firefox一样。 Chrome beta对于短时间内的点击做了通常的操作,但是在长时间接触的情况下,它不再触发mouseup事件,只有在半秒钟后才会触发touchstart,然后是mousedown,最后是当手指抬起时的touchend。当手指移动时,它的行为现在就像Firefox和Opera Mobile一样。
双击的情况下,它不会在缩小时触发触摸事件,只会在放大时触发。
Chrome beta的行为最奇怪,但我不能抱怨,因为它是一个beta版。

问题是: 是否有一种简单的方法可以在大多数触摸设备最常用的浏览器中尝试检测短按、长按和双击?

很遗憾,我无法在使用Safari的iOS设备或Windows Phone 7/Phone 8/RT上测试它,但如果你们中的一些人可以,你们的反馈将非常感谢。


1
你尝试过Tocca.js吗?http://gianlucaguarini.github.io/Tocca.js/ 它可以在任何设备上启用所有缺失的触摸事件,而且只有大约1kb。 - Gianluca Guarini
4个回答

26

如果你还没有阅读过 Hammer.js 的源代码,我建议你去阅读一下。

https://github.com/hammerjs/hammer.js/blob/master/hammer.js

在注释和代码之间,大约有 1400 行代码。这里有很好的文档,而且代码易于理解。

你可以看到作者是如何解决很多常见的触摸事件的:

hold(长按),tap(轻触),doubletap(双击),drag(拖动),dragstart(开始拖动),dragend(拖动结束),dragup(向上拖动),dragdown(向下拖动),dragleft(向左拖动),dragright(向右拖动),swipe(滑动),swipeup(向上滑动),swipedown(向下滑动),swipeleft(向左滑动),swiperight(向右滑动),transform(变换),transformstart(开始变换),transformend(变换结束),rotate(旋转),pinch(缩放),pinchin(收缩),pinchout(扩展),touch(手势检测启动),release(手势检测结束)

我认为,在阅读源代码之后,你会更好地了解触摸事件的工作原理,以及如何确定浏览器能够处理哪些事件。

http://eightmedia.github.io/hammer.js/


有趣的代码片段,看起来相当完整和广泛。我可能需要花些时间分析它。 - MaxArt

9

有一份非常出色的资源https://patrickhlauke.github.io/touch/tests/results/,详细介绍了众多浏览器事件的顺序。该资源似乎定期更新(2016年9月,最近更新于2016年8月)。

要点是:基本上所有内容都会触发mouseover和相关事件;大多数也会触发触摸事件,这些事件通常在mouseover之前完成(达到touchend),然后继续click(除非页面内容的更改取消了这一点)。这些尴尬的例外情况相对较少(第三方安卓浏览器和黑莓平板电脑)。

该链接资源提供了令人印象深刻的详细信息,以下是众多操作系统、设备和浏览器测试的前三个样本:

enter image description here

总结一些关键点:

移动浏览器

  • 所有列出的浏览器在第一次点击时都会触发mouseover。只有某些Windows Phone浏览器会在第二次点击时触发它。
  • 所有浏览器都会触发click。如果mouseover更改页面,则不指定哪个会取消click(我相信大多数浏览器都会这样做)。
  • 大多数浏览器在touchstarttouchend之后会触发mouseover。这包括iOS7.1 Safari,原生Android,Chrome,Opera和Firefox for Android,以及一些(不是所有的)Windows手机浏览器。
  • 几个Windows Phone浏览器(所有Windows 8 / 8.1和一个版本的10)和几个第三方Android浏览器(Dolphin,Maxathon,UC)会在touchstarttouchend之后触发mouseover
  • 只有Blackberry Playbook会在touchstarttouchend之间触发mouseover
  • 只有Opera Mini和Puffin(第三方Android浏览器)缺少touchstarttouchend

桌面浏览器

  • 桌面版Chrome和Opera的合理版本与其移动端相似,touchstarttouchend后跟mouseover
  • Firefox和Microsoft浏览器(IE <= 11和许多版本的Edge)不会触发任何touchstarttouchend事件。
  • 没有Mac数据,但考虑到Mac触摸屏接口的稀缺性,可能没有Ma浏览器支持touchstarttouchend

还有大量关于与辅助技术结合使用的浏览器数据。


3

是的,您可以在touchstart时启动计时器,在touchend时结束计时器并从那里进行选择。

此外,您可以使用touchmove触发滑动操作,获取手指的坐标,并查看在touchend被触发之前移动了多少。

我不知道是否有比使用触摸事件库更简单的方法,但我想您可以轻松地为简单的“点击”、“双击”、“滑动”事件编写一个库。


1
如果你仔细考虑一下浏览器的行为,你会发现这并不容易。例如,如果你触摸一个元素,然后将手指移出元素,再抬起手指,那就不是有效的“轻敲”,但由于 touchleave 没有被触发,你无法检测到它。 - MaxArt
你说得很有道理,但是;通过正确的编码,例如在touchstart时捕获元素e.target(我相信),它是否等于touchend上的元素?如果不是,则不是有效的轻触,而是陷阱。我并不是说编写一个库很容易,但它并不是真正的“困难”,我会将其排名为“中等”+如果没有挑战和让我们返回stackoverflow,那还有什么乐趣呢xD - Alexandru Calin
编写一个库对我来说不是问题,我觉得它具有挑战性、令人满意且有趣。但是你的评论只是暗示了一个解决方案,但是如何构建它呢?你在哪里保存关于 touchstart 事件的信息?如果同时触发第二个 touchstart 事件(多点触控显示器),该怎么办?如何处理长按?使用 setTimeout,但是如果手指同时离开元素会怎样?如果没有触摸事件,如何检测它?等等...当然我会尝试,但我希望找到关于触摸事件的深入分析。 - MaxArt
你不能在 touchend 事件被触发之前再次触发 touchstart 事件,因此你不能同时进行两次轻拍,所以这不是问题。你可以将信息保存在变量中,并在 touchend 时重置它们。 - Alexandru Calin
这是错误的,你可以在第一个touchend发生之前有第二个touchstart。此外,重新思考你的答案会得出结论,它并没有帮助,因为touchend事件的target属性始终等于触摸开始的元素,即使手指移动到元素外面也是如此! - MaxArt

3

以下是我对Android 4.3上触摸和鼠标事件的最新观察:

Opera、Firefox和Chrome似乎有一个标准行为

  1. 在滑动操作(touchstart-touchmove-touchend)时:

    1. 不会触发任何鼠标事件(除了mouseover)。
    2. 只有在touchstart和touchend发生在同一元素上时才会触发mouseover事件(touchstart-touchmove-touchend-mouseover)。
    3. 如果在touchstart上阻止默认行为:默认的滑动行为将无法工作。关于鼠标事件的触发不会发生任何变化。
  2. 在轻触(touchstart-touchend)时:

    1. 所有鼠标事件mouseover-mousemove-mousedown-mouseup-click都会延迟后触发。
    2. 如果在touchstart上阻止默认行为:只有mouseover事件会触发。

Android默认浏览器具有一些非标准行为

  1. mouseover事件会在touchstart之前触发,这意味着mouseover事件总是会触发。
  2. 在轻触时会触发所有鼠标事件,即使在touchstart上阻止了默认行为。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接