click延时
在移动设备上按下手指单击,按先后顺序,依次会发生touchstart->-touchmove(如果有的话)>touchend->mousedown->mousemove(如果有的话)->mouseup->click->dblckick(如果有的话,IOS上不支持dblclick事件,Android支持dblclick事件)。
在2007年苹果发布的首款iphone时,其IOS系统的safari,为了将PC端页面在手机显示时避免文字太小,就使用了双击缩放。在屏幕上快速连续单击两次,safari的浏览器会将网页放大或者缩小。而所谓click的“300mm”延时就出在这里,当用户按下一个手指单击时,safari会捕获这个click事件,但是并不会立马响应其处理函数,因为浏览器还不确定用户按了这次是不是还要接着按下一次来个双击缩放页面。如果是一个a超链接,单击这个超链接,单击了之后会在300毫秒之后才跳转,300毫秒之内又单击了就认定是个双击缩放页面,而不会跳转。
现在的IOS和Android的浏览器,还是存在这个特性,手指单击之后,最先触发touchstart,差不多300毫秒后click事件才发生。要解决这个延时的问题很简单,并不需要借助zepto还是其他的库。这个关键就在于,必需禁止移动浏览器自带的双击缩放功能,只要禁止之后,这个click延时就不存在。
给文档加这个头禁止页面缩放。对于IOS的safari必需要width=device-width属性,否则IOS中click就会延时。Android中,user-scalable=no和width=device-width两个中有一个就可以取消click延时,为了兼容性两个都写上。
通过标签取消了click 300毫秒延时之后,当按下手指,最先触发touchstart,然后再触发click时,最短可以只有10毫秒,只要松手指松的足够快,click就会足够的块的响应,松的慢就响应的慢。只有当手指最终松开时click事件才会发生,但IOS和Android对click的这个处理还是有较大差异,在Android上手指按下去一直不放,差多过了八百毫秒之后,手指再放开后click就不会发生了。IOS上手指一直按住,按多长时间后再放开,click事件还会发生。
这就存在一种不确定性,如果用click关闭一个遮罩层,手指松的早就关的早,松的迟遮罩就关的迟,在IOS一直按住按了10秒再松开,click还被触发让遮罩关闭了,能够按10秒基本都是要长按,都不是单击了。
对于要实现tap这个事件,就需要两个核心,第一个是规定一个时间,比如150毫秒之内松开手指就触发tap事件,松慢了tap就不会触发而认为这是要长按,第二点是tap必需在click事件的后面执行,如果tap在click前面执行就会产生“点透”的问题。
如果不设置标签放任浏览器的click延时,也可以解决单击延时。所有诸如zepto.touch.js这类库,自定义tap事件,它本质上就是没有使用click了,不管click延时不延时,它就是在使用touched来单击,把用户定义的单击操作放到了touchend中进行,不管有没有meta,touched不会有延时。
早期版本的zepto的tap就是如此处理的,tap自定义事件在touchend中触发,解决单击延时的问题。zeopto解决了延时,但是又由于touched会在click之前触发,这就又存在“点透”的问题。最新的1.2版zepto.touch.js试图去解决tap点透的问题,于是在touchend中加了一个setTimeout(callback,0)的异步事件。
但是它不见得好用,第一个问题是,如果没有设置meta禁止页面缩放让click保持在300毫秒后触发,那么设置不设置这个setTimeout()异步没有区别,touchend触发时候,用户定义的tap事件也就触发了,300毫秒后click又接着触发,还是会产生点透。zepto的tap的这个前提,就要求你必需设置meta让click尽快触发,但是即便设置了meta,zepto.touch.js还是有个bug会导致点透。在它的源码中,就像上面那样,给setTimeout()设置了0的延时,0延时的异步在PC上没有问题,在手机上就有问题,tap会有时会在click前面执行有时又会在click后面执行导致有时会点透。
tap可以借助touched解决单击延时,但是本质上没有办法解决点透,除非浏览器允许取消后面要跟随触发的click。要解决点透,只能设置让click尽快响应,然后再利用setTimout(callback,30)的异步回调让用户自定义tap在click触发之后再执行,这样才可以真正避免点透。
同时touched也是在手指最终松开时才触发,这会同样就会产生不确定性,因此也需要一个时间限制,超过了这个时间tap就不再触发。zepto.touch.js做了这个处理,但好像是疏忽写错了导致这个时间判断有问题。
点透
“点透”的问题主要存在于一个元素覆盖在另外一个元素上面时,最常见的就是遮罩层。给上面的遮罩层绑定touchstart之类的事件,在按下手指后,这类touchstart还是touchend都会在click之前发生,就会产生所谓点透。
在上图的单击按钮上面,通过touchstart在按下手指后立即关闭遮罩,没有延时关闭迅速,但是在手指按下的地方又接着发生了click事件,而此时遮罩层都已经关闭了又还发生了click事件,导致click事件被点在button按钮上,按钮的click事件处理函数就被触发了,如果这里不是button,而是个input,会导致点这里关闭了遮罩而下面的input输入框被聚焦弹出了输入键盘,这就是“点透”。在单页应用,也会一个页面都切换了,click被点了新的页面上。
移动端实现tap自定义事件
对于自定义DOM事件通过btn.addEventListener('tap',callback,false),使用浏览器原生addEventListener()绑定,这要通过CustomEvent()类来实现。
var btn=document.getElementById('btn');
//给button按钮绑定tap事件
btn.addEventListener('tap',function(){
alert('tap事件执行');
},false)
//自定义tap DOM事件
var event=new CustomEvent('tap',{
bubbles: true,
cancelable: true
});
//触发btn上的tap事件
btn.dispatchEvent(event);
当然实际中btn.dispatchEvent(event)肯定要放在其他的事件回调中,不然没办法响应用户操作。
如果只是为了要tap,这段代码可以取代引入zepto、zeopo.touch还是其他的这类文件。当然由于也是借助touched,因此前提也必需设置禁止页面缩放才能避免点透。其中给setTimeout()设置了30毫秒的延时,实际上手机浏览器计时并不准确,延时定短了tap有可能就在click前面执行了。虽然松开手指时touchend和click会一前一后触发,但之间的间隔并不是每次都一样,少的时候只有几毫米,多的时候有二三十毫秒,因此tap需要延时在30毫秒之后,保证它在click之后执行。
如果要在pc上也兼容可以再通过mousedown、mouseup、mousemove来处理,原理一样,只是做个pc与移动判断。如果要实现其他的诸如,手指左滑动右滑动事件还是长按等等之类自定义事件,原理基本差不多。