最近接触了几个移动web活动页面的制作,不算复杂,用不上类似angular的框架,自己决定使用哪些js库,很自由。说一下为此了解的几个问题。

click事件在移动设备上

click事件在web页面中有300ms的延迟,原因和历史大家知道,当初iphone流行时考虑到双指点击缩放的问题,需要等待300ms来确定用户是单击还是双击,因此造成的延迟在设备上还是比较明显

1
2
3
4
5
6
7
8
9
10
11
12
13
var body = document.querySelector('body');
var st,et,ct;
body.addEventListener('touchstart', function(){
st = Date.now();
});
body.addEventListener('touchend', function(){
et = Date.now();
});
body.addEventListener('click', function(){
ct = Date.now();
console.log("touchstart gap: " + (ct-st)); //301
console.log("touchend gap: " + (ct-et)); //173
});

解决过程

zepto touch

页面用使用zepto的话,建议使用touch模块中的tap方法,此模块还提供了’swipe’, ‘swipeLeft’, ‘swipeRight’, ‘swipeUp’, ‘swipeDown’,’doubleTap’, ‘tap’, ‘singleTap’, ‘longTap’等方法

1
2
3
4
5
touchTimeout = setTimeout(function(){
touchTimeout = null
if (touch.el) touch.el.trigger('singleTap')
touch = {}
}, 250)

以上是部分源码,它是通过添加touchstart[touchstart MSPointerDown pointerdown], touchmove[touchmove MSPointerMove pointermove] touchend[touchend MSPointerUp pointerup]事件监听,激发Tap事件条件可能有两个:
1.没有激发touchmove事件则直接激发
2.激发了touchmove事件则要在移动误差deltaX和deltaY小于30
实测效果可以,但依赖zepto

另外还有一个很严重的问题

1
2
$(document)
.bind('MSGestureEnd', function(e){}

touch模块中将所有的事件都代理到document上

fastclick

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
FastClick.attach(document.body);
var body = document.querySelector('body');
var st,et,ct;
body.addEventListener('touchstart', function(){
st = Date.now();
});
body.addEventListener('touchend', function(){
et = Date.now();
});
body.addEventListener('click', function(){
ct = Date.now();
console.log("touchstart gap: " + (ct-st)); //130
console.log("touchend gap: " + (ct-et)); //NaN
});

基本上touchend之后立即触发,另外fastclick相比于tap还有如下优点:fastclick与zepto tap对比

自定义点击事件

如果要做的页面很少,想想fastclick 26k的大小,还是不想用,另外业务需求,要加入点击效果,so想想还是自定义一个吧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
window.Tap = function(p){
this.target = p.target ;
this.tapCallBack = p.tapCallBack || function(){};
this.startCallBack = p.startCallBack || function(){};
this.endCallBack = p.endCallBack || function(){};
this.cancelCallBack = p.cancelCallBack || function(){};
};
Tap.prototype.addTapEvent = function(){
var touch = {};
this.target.addEventListener('touchstart', function(e){
this.startCallBack(this.target);
touch.x1 = e.touches[0].pageX;
touch.y1 = e.touches[0].pageY;
touch.last = Date.now();
}.bind(this));
this.target.addEventListener('touchmove', function(e){
touch.x2 = e.touches[0].pageX;
touch.y2 = e.touches[0].pageY;
}.bind(this));
this.target.addEventListener('touchend', function(e){
this.endCallBack(this.target);
/*模拟zepto tap事件, 分为touchmove激发和不激发两种情况*/
if( (Date.now() - touch.last < 250) ){
if(touch.x2 && touch.y2){
if( Math.abs(touch.x1 - touch.x2) < 30 && Math.abs(touch.y1 - touch.y2) < 30 ){
this.tapCallBack(this.target);
}
}else{
this.tapCallBack(this.target);
}
}
touch = {};
}.bind(this));
this.target.addEventListener('touchcancel', function(){
touch = {};
this.cancelCallBack(this.target);
}.bind(this));
}

定义了按下回调,弹起回调,点击回调和取消回调几个函数;考虑到会被多次调用,必须私有化这几个变量,所以定义在构造函数中,其他模拟zepto tap,但将点击直接绑定在元素本身,为了保证在事件回调函数中访问到外面的this, 利用bind关键字改变函数this上下文,这是bind常用的地方。
实测效果不错,自定义效果运行正常,

注意touch事件的流程
1.touchstart -》 touchend
2.touchstart -》 touchmove -》touchend
3.touchstart -》 touchcancel
4.2.touchstart -》 touchmove -》touchcancel
也就是说当touch事件被打断,touchcancel被触发时(如弹出复制选框),touchend事件不会被执行。所以在cancel事件中要恢复样式。或者在touchstart事件中调用e.preventDefault()使得不会触发touchCancel。样式会自动返回。但这样会造成一系列问题:如果绑定的click事件不会被执行。不推荐这么做