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