Safari 为 iOS 定制了一些专有事件,以方便开发者。因为 iOS 设备没有鼠标和键盘,所以常规的鼠标和键盘事件不足以创建具有完整交互能力的网页。同时,WebKit 也为 Android 定制了很多专有事件,成为了事实标准,并被纳入 W3C 的 Touch Events 规范。本节介绍的事件只适用于触屏设备。 触摸事件 iPhone 3G 发布时,iOS 2.0 内置了新版本的 Safari。这个新的移动 Safari 支持一些与触摸交互有关的新事件。后来的 Android 浏览器也实现了同样的事件。当手指放在屏幕上、在屏幕上滑动或从屏幕移开时,触摸事件即会触发。触摸事件有如下几种。  touchstart:手指放到屏幕上时触发(即使有一个手指已经放在了屏幕上)。  touchmove:手指在屏幕上滑动时连续触发。在这个事件中调用 preventDefault()可以阻止 滚动。  touchend:手指从屏幕上移开时触发。  touchcancel:系统停止跟踪触摸时触发。文档中并未明确什么情况下停止跟踪。 这些事件都会冒泡,也都可以被取消。尽管触摸事件不属于 DOM 规范,但浏览器仍然以兼容 DOM的方式实现了它们。因此,每个触摸事件的 event 对象都提供了鼠标事件的公共属性:bubbles、 cancelable、view、clientX、clientY、screenX、screenY、detail、altKey、shiftKey、 ctrlKey 和 metaKey。 除了这些公共的 DOM 属性,触摸事件还提供了以下 3 个属性用于跟踪触点。  touches:Touch 对象的数组,表示当前屏幕上的每个触点。  targetTouches:Touch 对象的数组,表示特定于事件目标的触点。  changedTouches:Touch 对象的数组,表示自上次用户动作之后变化的触点。 每个 Touch 对象都包含下列属性。  clientX:触点在视口中的 x 坐标。  clientY:触点在视口中的 y 坐标。  identifier:触点 ID。  pageX:触点在页面上的 x 坐标。  pageY:触点在页面上的 y 坐标。  screenX:触点在屏幕上的 x 坐标。  screenY:触点在屏幕上的 y 坐标。  target:触摸事件的事件目标。 这些属性可用于追踪屏幕上的触摸轨迹。例如:

// 只针对一个触点
 if (event.touches.length == 1) { 
 let output = document.getElementById("output"); 
 switch(event.type) { 
 case "touchstart": 
 output.innerHTML += `<br>Touch started:` + 
 `(${event.touches[0].clientX}` + 
 ` ${event.touches[0].clientY})`; 
 break; 
 case "touchend": 
 output.innerHTML += `<br>Touch ended:` + 
 `(${event.changedTouches[0].clientX}` + 
 ` ${event.changedTouches[0].clientY})`; 
 break; 
 case "touchmove": 
 event.preventDefault(); // 阻止滚动
 output.innerHTML += `<br>Touch moved:` + 
 `(${event.changedTouches[0].clientX}` + 
 ` ${event.changedTouches[0].clientY})`; 
 break; 
 } 
 } 
} 
document.addEventListener("touchstart", handleTouchEvent); 
document.addEventListener("touchend", handleTouchEvent); 
document.addEventListener("touchmove", handleTouchEvent);

以上代码会追踪屏幕上的一个触点。为简单起见,代码只会在屏幕有一个触点时输出信息。在touchstart 事件触发时,触点的位置信息会输出到 output 元素中。在 touchmove 事件触发时,会取消默认行为以阻止滚动(移动触点通常会滚动页面),并输出变化的触点信息。在 touchend 事件触发时,会输出触点最后的信息。注意,touchend 事件触发时 touches 集合中什么也没有,这是因为没有滚动的触点了。此时必须使用 changedTouches 集合。 这些事件会在文档的所有元素上触发,因此可以分别控制页面的不同部分。当手指点触屏幕上的元素时,依次会发生如下事件(包括鼠标事件): (1) touchstart (2) mouseover (3) mousemove(1 次) (4) mousedown (5) mouseup (6) click (7) touchend 手势事件 iOS 2.0 中的 Safari 还增加了一种手势事件。手势事件会在两个手指触碰屏幕且相对距离或旋转角度 变化时触发。手势事件有以下 3 种。  gesturestart:一个手指已经放在屏幕上,再把另一个手指放到屏幕上时触发。  gesturechange:任何一个手指在屏幕上的位置发生变化时触发。  gestureend:其中一个手指离开屏幕时触发。 只有在两个手指同时接触事件接收者时,这些事件才会触发。在一个元素上设置事件处理程序,意味着两个手指必须都在元素边界以内才能触发手势事件(这个元素就是事件目标)。因为这些事件会冒泡,所以也可以把事件处理程序放到文档级别,从而可以处理所有手势事件。使用这种方式时,事件的目标就是两个手指均位于其边界内的元素。 触摸事件和手势事件存在一定的关系。当一个手指放在屏幕上时,会触发 touchstart 事件。当另一个手指放到屏幕上时,gesturestart 事件会首先触发,然后紧接着触发这个手指的 touchstart事件。如果两个手指或其中一个手指移动,则会触发 gesturechange 事件。只要其中一个手指离开屏幕,就会触发 gestureend 事件,紧接着触发该手指的 touchend 事件。 与触摸事件类似,每个手势事件的 event 对象都包含所有标准的鼠标事件属性:bubbles、cancelable、view、clientX、clientY、screenX、screenY、detail、altKey、shiftKey、 ctrlKey 和 metaKey。新增的两个 event 对象属性是 rotation 和 scale。rotation 属性表示手指变化旋转的度数,负值表示逆时针旋转,正值表示顺时针旋转(从 0 开始)。scale 属性表示两指之间距离变化(对捏)的程度。开始时为 1,然后随着距离增大或缩小相应地增大或缩小。 可以像下面这样使用手势事件的属性:

let output = document.getElementById("output"); 
 switch(event.type) { 
 case "gesturestart": 
 output.innerHTML += `Gesture started: ` + 
 `rotation=${event.rotation},` + 
 `scale=${event.scale}`;
break; 
 case "gestureend": 
 output.innerHTML += `Gesture ended: ` + 
 `rotation=${event.rotation},` + 
 `scale=${event.scale}`; 
 break; 
 case "gesturechange": 
 output.innerHTML += `Gesture changed: ` + 
 `rotation=${event.rotation},` + 
 `scale=${event.scale}`; 
 break; 
 } 
} 
document.addEventListener("gesturestart", handleGestureEvent, false); 
document.addEventListener("gestureend", handleGestureEvent, false); 
document.addEventListener("gesturechange", handleGestureEvent, false);

与触摸事件的例子一样,以上代码简单地将每个事件对应到一个处理函数,然后输出每个事件的信息。