最近接触了几个移动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事件不会被执行。不推荐这么做