在 iOS 的 Safari 浏览器中,增加了一些触摸(touch)事件和手势(gesture)事件,这里总结一下它们的用法。
一、触摸事件
iOS 浏览器的触摸事件包括 touchstart,touchmove,touchend,touchcancel。Android 的浏览器中也同样支持这些事件。这些触摸事件的触发条件如下:
- touchstart:手指刚接触屏幕时触发
- touchmove:手指在屏幕上移动时触发
- touchend:手指从屏幕上移开时触发
- touchcancel:触摸过程被系统取消时触发
这些触摸事件都包含了如下这几个列表:
- touches:位于屏幕上的所有手指的列表
- targetTouches:位于该元素上的所有手指的列表
- changedTouches:涉及当前事件的所有手指的列表
而这些列表中的每一项都包含如下这些属性:
- clientX:相对于视区的 X 坐标
- clientY:相对于视区的 Y 坐标
- screenX:相对于屏幕的 X 坐标
- screenY:相对于屏幕的 Y 坐标
- pageX:相对于页面的 X 坐标
- pageY:相对于页面的 Y 坐标
- identifier: 当前触摸点的惟一编号
- target: 手指所触摸的 DOM 元素
例如,下面的代码使你可以用单指触摸来拖动一个 DOM 元素:
var obj = document.getElementById("id"); obj.addEventListener("touchmove", function(event) { if (event.targetTouches.length == 1) { var touch = event.targetTouches[0]; obj.style.left = touch.pageX + "px"; obj.style.top = touch.pageY + "px"; } }, false);
总之,对于手指对页面元素的触摸过程,我们可以通过 touchstart,touchmove 和 touchend 这三个事件来处理各种触摸交互。
二、传统事件
为了保证在 Mobile Safari 浏览器中能正常地使用原有的网页,在触摸完成后一般也会触发鼠标和键盘事件。按照苹果公司的文档[1],如果该页面元素是可点击元素(clickable element),那么在 touchstart,touchmove 和 touchend 事件后将触发 mouseover 和 mousemove 事件。如果在这两个事件后该元素的内容不改变,将继续触发 mousedown,mouseup,和 click 事件。如果再触摸另一个可点击元素,之前的元素将触发 mouseout 事件。
至于什么元素属于可点击元素,Mobile Safari 认为链接元素,表单元素和图像映射区域是可点击元素,以及绑定了 mousemove,mousedown,mouseup 或 click 事件处理程序的元素是可点击元素。其它元素都不是可点击元素。原文如下:A clickable element is a link, form element, image map area, or any other element with mousemove, mousedown, mouseup, or onclick handlers.
Mobile Safari 中对传统事件的这些兼容方式,可以保证大多数针对桌面设备设计的网页仍然能正常工作。但是问题还是有的。
其一,这种兼容方式将导致 click 事件的延迟触发,延迟的时间在 300ms 左右。如果你的网页对实时性要求较高,那只能直接处理 touch 事件,而不要依赖于 click 事件。
其二,非可点击元素将不会触发上述的鼠标键盘事件。这个原则同样适用于使用了 CSS :hover 伪类的情形,例如纯 CSS 下拉菜单。这个问题可以通过添加空的 click 事件处理程序来解决。例如:<span onclick="void(0)">Hover Me</span>。同样,要让 :hover 样式能够取消,等同于该元素能够触发 mouseout 事件。我们可以给其它兄弟元素都添加空的 click 事件处理程序,使得点击任何其它地方时触发该元素的 mouseout 事件。
其三,在默认情形 Safari 认为 body 元素为不可点击元素,从而 click 事件冒泡时不会冒泡到该元素。因此,我们常用的在 document 或者 body 元素中用事件代理方式统一处理所有元素事件的方法将会失效。这个古怪的问题可以通过在 CSS 中对 body 元素添加 cursor: pointer 样式来解决。
三、手势事件
iOS 的 Safari 中还支持一些手势事件,包括 gesturestart,gesturechange 和 gestureend。这些手势事件只在两个或更多手指触摸屏幕时触发。例如:
document.addEventListener("gesturechange", function(event) { event.preventDefault(); console.log("Scale: " + event.scale + ", Rotation: " + event.rotation); }, false);
可以看到,手势事件比触摸事件简单多了,它只提供了缩放比例和旋转角度这两个信息。如果需要处理更多的用户触摸操作,还是得用触摸事件。而且,Android 的浏览器不支持这些手势事件,这样手势事件就很鸡肋了。
四、特性检测
要检测浏览器是否支持触摸事件,可以用下面的简单代码:
var touchSupport = ("ontouchstart" in window)
参考资料:
[1] Safari Web Content Guide: Handling Events
[2] Touch events - Document Object Model (DOM) | MDN
[3] Tap vs. Click: Death by Ignorance
[4] The Current State of (Touch) Events
[5] Click event delegation on the iPhone
[6] Click event delegation on the iPhone — redux
[7] Mobile compatibility tables - quirkmodes
[8] Touch table - quirkmodes
[9] Event delegation for touch events in JavaScript
[A] Introducing Mobile Javascript Events and Creating an Interactive Experience
[B] Back To The Code: JavaScript Touch and Gesture Events iPhone and Android
[C] Touching and Gesturing on iPhone, Android, and More
[D] Multi-touch - Wikipedia #Multi-touch_gestures
[E] Multi-Touch Scale & Rotation Across Android Honeycomb and iOS
[F] Developing for Multi-Touch Web Browsers - HTML5 Rocks(翻译)
[G] 多触式web前端开发之一:对于Touch的处理
[H] 多触式web前端开发之二:处理简单手势
[I] 多触式web前端开发之三:处理复杂手势
[J] Modernizr: Touch tests
[K] detect a 'touch screen' device using JavaScript?
[L] Different ways to trigger touchcancel in mobile browsers