一佳鳖、click與300ms延遲
? ? 移動(dòng)端瀏覽器提供了一個(gè)特殊的功能:雙擊放大
300ms的延遲就是來(lái)自于這里吩案,用戶碰觸頁(yè)面之后棚赔,需要等待一段時(shí)間來(lái)判斷是不是雙擊(double tap)動(dòng)作,而不是立即響應(yīng)單擊(click),等待的這段時(shí)間大約是300ms徘郭。
之前有過簡(jiǎn)單介紹(黯羽輕揚(yáng):HTML5觸摸事件)
移動(dòng)事件提供了touchstart靠益、touchmove、touchend卻沒有提供tap支持残揉,主流框架(庫(kù))都是手動(dòng)實(shí)現(xiàn)了自定義tap事件胧后,以消除300ms延遲,提高頁(yè)面響應(yīng)速度抱环,對(duì)于簡(jiǎn)單的頁(yè)面壳快,可以把touchstart或者touchend當(dāng)做tap來(lái)用,但是存在一些問題镇草,比如手直接觸目標(biāo)元素眶痰,按住不放,慢慢移除響應(yīng)區(qū)域梯啤,會(huì)觸發(fā)touchstart事件執(zhí)行對(duì)應(yīng)的事件處理器(本不應(yīng)該觸發(fā))竖伯,touchend事件也存在類似的問題。
此外因宇,使用原生touch事件也存在點(diǎn)擊穿透的問題七婴,因?yàn)閏lick是在touch系列事件發(fā)生大約300ms才觸發(fā)的,混用touch和click肯定會(huì)導(dǎo)致點(diǎn)透問題察滑,下面詳細(xì)介紹
二本姥、點(diǎn)擊穿透問題
點(diǎn)擊穿透現(xiàn)象有三種
? ? *點(diǎn)擊穿透問題:點(diǎn)擊蒙層(mask)上的關(guān)閉按鈕,蒙層消失后發(fā)現(xiàn)觸發(fā)了按鈕下面元素的click事件
蒙層關(guān)閉按鈕綁定的是touch事件杭棵,而按鈕下邊元素綁定的是click事件,touch事件觸發(fā)之后,蒙層消失了魂爪,300ms后這個(gè)點(diǎn)的click事件fire先舷,event的target自然就是按鈕下面的元素,因?yàn)榘粹o跟蒙層一起消息了滓侍。
? ? *跨頁(yè)面點(diǎn)擊穿透事件:如果按鈕下面恰好是一個(gè)href屬性的a標(biāo)簽蒋川,那么頁(yè)面就會(huì)法神跳轉(zhuǎn),因?yàn)閍標(biāo)簽跳轉(zhuǎn)默認(rèn)是click事件觸發(fā)撩笆,所以原理和上面的完全相同
? ? *另一種跨頁(yè)面點(diǎn)擊穿透問題:這次沒有mask了捺球,直接點(diǎn)擊頁(yè)內(nèi)按鈕跳轉(zhuǎn)至新頁(yè),然后發(fā)現(xiàn)新頁(yè)面中對(duì)應(yīng)位置元素的click事件被觸發(fā)
和蒙層的道理一樣夕冲,js控制頁(yè)面跳轉(zhuǎn)的邏輯如果是綁定在touch事件上的氮兵,而且新頁(yè)面中對(duì)應(yīng)位置的元素綁定的是click事件,而且頁(yè)面在300ms內(nèi)完成了跳轉(zhuǎn)歹鱼,三個(gè)條件同時(shí)滿足泣栈,就出現(xiàn)這種情況了
非要細(xì)分的話還有第四種,不過概率很低弥姻,就是新頁(yè)面中對(duì)應(yīng)位置元素恰好是a標(biāo)簽南片,然后就發(fā)生連續(xù)跳轉(zhuǎn)了。庭敦。疼进。諸如此類的,都是點(diǎn)擊穿透問題
三秧廉、為什么會(huì)出現(xiàn)點(diǎn)透
click延遲伞广、延遲、還是延遲
在移動(dòng)端不使用click而用touch事件代替觸摸是因?yàn)閏lick事件有著明顯的延遲定血,具體touchstart與click的區(qū)別如下:
? ? *touchstart:在這個(gè)DOM(或者冒泡到這個(gè)DOM)上手指觸摸開始即能立即出發(fā)
? ? *click:在這個(gè)DOM(或者冒泡這個(gè)DOM)上手指觸摸開始赔癌,且手指未在屏幕上移動(dòng)(某些<a href=”http://www.it165.net/edu/ewl/” target=”_blank” class=”keylink”>瀏覽器</a>允許移動(dòng)一個(gè)非常小的位移值),且在這個(gè)DOM元素上手指離開屏幕澜沟,且觸摸和離開屏幕之間的間隔時(shí)間較短(某些瀏覽器不檢測(cè)間隔時(shí)間灾票,也會(huì)觸發(fā)click)才能觸發(fā)
也就是說,事件的觸發(fā)事件按照由早到晚排列為:touchstart早于touchend早于click茫虽。亦即click的觸發(fā)是有延遲的刊苍,這個(gè)時(shí)間大概在300ms左右(即使給元素綁定的是touch事件,touchstart=>touchend=>click濒析,click事件依然會(huì)被觸發(fā))
由于我們?cè)趖ouchstart階段就已經(jīng)隱藏了罩層A夹纫,當(dāng)click被觸發(fā)的時(shí)候递惋,能夠被點(diǎn)擊的元素則是罩層下面的B元素綁定的事件,根據(jù)click事件的觸發(fā)規(guī)則:
只有在被觸發(fā)的時(shí)候,當(dāng)前有click事件的元素顯示素标,且在面朝用戶的最前端時(shí)七扰,才出發(fā)click事件。
由于B綁定了click事件(或者B本身默認(rèn)存在click事件),所以B的click事件被觸發(fā)荣暮,產(chǎn)生了點(diǎn)透的情況。
解決方案
對(duì)于B元素本身沒有默認(rèn)click事件的情況(無(wú)a標(biāo)簽等)罩驻,應(yīng)統(tǒng)一使用touch事件穗酥,統(tǒng)一代碼風(fēng)格,并且由于click事件在移動(dòng)端的延遲要大很多惠遏,不利于用戶體驗(yàn)砾跃,所以關(guān)于觸摸事件應(yīng)盡量使用touch相關(guān)事件。
對(duì)于B元素本身存在默認(rèn)click事件的情況,應(yīng)及時(shí)取消A元素的默認(rèn)點(diǎn)擊事件节吮,從而阻止click事件的產(chǎn)生抽高。即應(yīng)在上例的handle函數(shù)中添加代碼如下:
對(duì)于遮蓋浮層,由于遮蓋浮層的點(diǎn)擊即使有小延遲也是沒有關(guān)系的课锌,反而會(huì)有疑似更好的用戶體驗(yàn)厨内,所以這種情況,可以針對(duì)遮蓋浮層自己采用click事件渺贤,這樣就不會(huì)出現(xiàn)點(diǎn)透問題雏胃。
四、解決方案
問題已經(jīng)很明了了志鞍,有很多解決方案瞭亮,但是思路不外乎2種:
1、不要混用touch和click
? ? ? ? 既然touch之后300ms會(huì)觸發(fā)click固棚,只用touch或者只用click自然不會(huì)存在問題
2统翩、吃掉或者消費(fèi)掉touch之后的click
? ? ? ? 依舊用tap,只是在可能發(fā)生點(diǎn)擊穿透的情形做額外的處理此洲,拿個(gè)東西來(lái)?yè)踝』蛘遲ap后延遲350ms在隱藏mask厂汗、pointer-events、在下面元素的事件處理器里做檢測(cè)(配合全局flag)等等呜师,能吃掉就行
詳細(xì)解決方案
1娶桦、只用touch
? ? 最簡(jiǎn)單的解決方案,完美解決點(diǎn)擊穿透事件
把頁(yè)面內(nèi)所有click全部換成touch事件(touchstart汁汗、touchend衷畦、tap),需要特別注意a標(biāo)簽,a標(biāo)簽的href也是click知牌,需要去掉換成js控制的跳轉(zhuǎn)祈争,或者直接改成span+tap控制跳轉(zhuǎn)。如果要求不高角寸,不在乎滑走或者滑進(jìn)來(lái)觸發(fā)事件的話菩混,span+touchend就可以了忿墅。畢竟tap需要引入第三方庫(kù)
不用a標(biāo)簽其實(shí)沒什么,移動(dòng)app開發(fā)不用考慮SEO沮峡,即便用了a標(biāo)簽球匕,一般也會(huì)去掉所有默認(rèn)樣式,不如直接用span
2帖烘、只是用click
下下策,因?yàn)閹?lái)300ms延遲橄杨,頁(yè)面內(nèi)任何一個(gè)滴定儀監(jiān)護(hù)都將增加300ms延遲秘症,想想都慢
不用touch就不會(huì)存在touch之后300ms觸發(fā)click的問題,如果交互性要求不高可以這么做式矫,?強(qiáng)烈不推薦?乡摹,快一點(diǎn)總是好的
3、拿個(gè)東西來(lái)?yè)踝?/p>
比較笨的方法采转,不推薦用
4聪廉、tap后延遲350ms在隱藏mask
改動(dòng)最小,缺點(diǎn)是隱藏mask變慢了故慈,350ms還是能感覺到慢的
只需要針對(duì)mask做處理就行板熊,改動(dòng)非常小,如果要求不高的話察绷,用這個(gè)比較省力
5干签、pointer-events
比較麻煩且有缺陷,不建議使用
mask隱藏后拆撼,給按鈕下面元素添加上pointer-events:none;樣式容劳,讓click穿過去,350ms后去掉這個(gè)樣式闸度,恢復(fù)響應(yīng)
缺陷是mask消失后的的350ms內(nèi)竭贩,用戶可以看到按鈕下面的元素點(diǎn)著沒反應(yīng),如果用戶手速很快的話一定會(huì)發(fā)現(xiàn)
在下面元素的事件處理器里做檢測(cè)(配合全局flag)
6莺禁、比較麻煩留量,?不建議使用
全局flag記錄按鈕點(diǎn)擊的位置(坐標(biāo)點(diǎn)),在下面元素的事件處理器里判斷event的坐標(biāo)點(diǎn)睁宰,如果相同則是那個(gè)可惡的click肪获,拒絕響應(yīng)
上面說的只是想法,沒測(cè)試過柒傻,實(shí)在不行就用記錄時(shí)間戳判斷孝赫,等待350ms,這樣就和?pointer-events?差不多
7红符、fastclick
好用的解決方案青柄,不介意多加載幾kb的話伐债,不建議使用,因?yàn)橛腥擞龅搅薭ug致开,更多信息請(qǐng)查看:Fastclick 導(dǎo)致click事件觸發(fā)兩次的問題
首先引入fastclick庫(kù)峰锁,再把頁(yè)面內(nèi)所有touch事件都換成click,其實(shí)稍微有點(diǎn)麻煩双戳,建議引入這幾KB就為了解決點(diǎn)透問題不值得虹蒋,不如用第一種方法呢
參考文檔:http://www.uedsc.com/through-the-click-point-in-the-development-of-web.html