收藏 | 點(diǎn)贊
一、前言
相信很多移動(dòng)開(kāi)發(fā)者都有這樣一個(gè)體會(huì),那就是我們?cè)谝苿?dòng)端點(diǎn)擊事件click對(duì)比touchend會(huì)有延遲,這是為什么呢?
其實(shí)這是因?yàn)橛[器在click后會(huì)等待約300ms去判斷用戶是否有雙擊行為,手機(jī)需要知道用戶是不是想雙擊放大網(wǎng)頁(yè)內(nèi)容。如果300ms內(nèi)沒(méi)有再一次click,那么就判定這是一次單擊行為,所以我們基本上都用(touchstart/touchend),但是這些事件在執(zhí)行完之后還會(huì)執(zhí)行一次click事件。(至于具體的原因這要從JS事件監(jiān)聽(tīng)機(jī)制的根本的講起,解釋起來(lái)太麻煩,感興趣的同學(xué)可以動(dòng)手了解一下,我們這里就不做過(guò)多說(shuō)明了)。
二、touch事件的來(lái)源
PC網(wǎng)頁(yè)上的大部分操作都是用鼠標(biāo)的,即響應(yīng)的是鼠標(biāo)事件,包括mousedown、mouseup、mousemove和click事件。一次點(diǎn)擊行為,事件的觸發(fā)過(guò)程為:mousedown -> mouseup -> click 三步。
因?yàn)槭謾C(jī)上沒(méi)有鼠標(biāo),所以就用觸摸事件touch去實(shí)現(xiàn)類似的功能。touch事件包含touchstart、touchmove、touchend,注意手機(jī)上并沒(méi)有tap事件。手指觸發(fā)觸摸事件的過(guò)程為:touchstart -> touchmove -> touchend。
手機(jī)上沒(méi)有鼠標(biāo),但不代表手機(jī)不能響應(yīng)mouse事件,其實(shí)是借助touch去觸發(fā)mouse事件。有人在PC和手機(jī)上對(duì)事件做了對(duì)比實(shí)驗(yàn),以說(shuō)明手機(jī)對(duì)touch事件相應(yīng)速度快于mouse事件。
從上面的圖表對(duì)比中我們可以看出在手機(jī)上,當(dāng)我們手觸碰屏幕時(shí),要過(guò)300ms左右才會(huì)觸發(fā)mousedown事件,所以click事件在手機(jī)上看起來(lái)就像慢半拍一樣。
三、點(diǎn)擊穿透的場(chǎng)景
點(diǎn)擊穿透的現(xiàn)象主要分為四種:
1.點(diǎn)擊蒙層(Mask Layer)上的關(guān)閉按鈕,蒙層消失后發(fā)現(xiàn)觸發(fā)了按鈕下面元素的click事件。
蒙層的關(guān)閉按鈕綁定的是touch事件,而按鈕下面元素綁定的是click事件,touch事件觸發(fā)之后,蒙層消失了,300ms后這個(gè)點(diǎn)的click事件出發(fā),事件的目標(biāo)元素自然就變成了按鈕下面的元素,因?yàn)榘粹o跟蒙層一起消失了。
2.如果按鈕下面恰好是一個(gè)有href屬性的a標(biāo)簽,那么頁(yè)面就會(huì)發(fā)生跳轉(zhuǎn),因?yàn)閍標(biāo)簽跳轉(zhuǎn)默認(rèn)是click事件觸發(fā),所以穿透原理和上面的完全相同。
3.如果按鈕下面恰好是文本框input或文本域textarea,則文本框或文本域就會(huì)獲取焦點(diǎn),穿透原理和上面的相同。
4.這次沒(méi)有蒙層,直接點(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)這種情況了。
其他的點(diǎn)擊穿透的例子還有很多,我就不一一細(xì)說(shuō)了。
四、Touch穿透的解決辦法
1.延遲
蒙層被點(diǎn)擊后延時(shí)至少300ms再在徹底隱藏掉蒙層顯示下層的內(nèi)容,缺點(diǎn)是隱藏蒙層變慢了,350ms還是能感覺(jué)到慢的,但是這種方法只需要針對(duì)蒙層做處理就行了,改動(dòng)非常小,如果要求不高的話,用這個(gè)比較省時(shí)省力。
2.增加中間蒙層
我們還可以動(dòng)態(tài)地在觸摸位置生成一個(gè)透明的元素,這樣當(dāng)上層元素消失而延遲的click來(lái)到時(shí),它點(diǎn)擊到的是那個(gè)透明的元素,就不會(huì)“穿透”到下面去了,然后再在一定的延遲后將生成的透明元素移除。
3.利用pointer-events方法
pointer-events是CSS3中的屬性,它有很多取值,auto | none | visiblepainted | visiblefill | visiblestroke | visible | painted | fill | stroke | all,有用的主要是auto和none,其他屬性值為SVG服務(wù)。
當(dāng)pointer-events取值為auto時(shí),效果與pointer-events屬性未指定時(shí)的表現(xiàn)效果相同;當(dāng)取值為none時(shí),元素永遠(yuǎn)不會(huì)成為鼠標(biāo)事件的目標(biāo),但是,當(dāng)其后代元素的pointer-events屬性指定其他值時(shí),鼠標(biāo)事件可以指向后代元素,在這種情況下,鼠標(biāo)事件將在捕獲或冒泡階段觸發(fā)父元素的事件偵聽(tīng)器。
$('.button').on('touchstart',function(){ $('.upbox').hide(); $('.underbox').hide(); //馬上讓它不能點(diǎn)擊 $('.underbox').css('pointer-events','none'); //因?yàn)閏lick事件需要300ms響應(yīng),所以我們時(shí)間定義360ms,時(shí)間一過(guò)又可以正常點(diǎn)擊了 setTimeout(function(){$('.underbox').css('pointer-events','auto')},360); });
4.只用click
頁(yè)面里的點(diǎn)擊事件都用click來(lái)觸發(fā),但是這樣的話,頁(yè)面里的點(diǎn)擊交互都將增加300ms延遲,想想都慢,但是如果交互性要求不高的話可以這么做,強(qiáng)烈不推薦 ,快一點(diǎn)總是好的。
5.使用fastclick插件
如果不介意多加載幾KB的話,可以使用fastclick庫(kù),其實(shí)現(xiàn)思路是:取消 click 事件,用 touchend 模擬快速點(diǎn)擊行為,從此所有點(diǎn)擊事件都使用click,不會(huì)出現(xiàn)“穿透”的問(wèn)題,并且沒(méi)有300ms的延遲。不建議使用,因?yàn)橛腥擞龅搅薭ug, fastclick 導(dǎo)致click事件觸發(fā)兩次的問(wèn)題,而且開(kāi)發(fā)者還必須先引入fastclick庫(kù),再把頁(yè)面內(nèi)所有touch事件都換成click,稍微有點(diǎn)麻煩,而且多引入幾KB的文件只是為了解決點(diǎn)透問(wèn)題不值當(dāng),不如用前三種方法中的任一種。
五、總結(jié)
除了上面這五種辦法外,相信還有其他我本文中未收錄的方法,這就需要大家一起來(lái)探索了,其實(shí)遇到問(wèn)題,第一重要的不是立即著手解決問(wèn)題,而是找到問(wèn)題的根源所在,之后針對(duì)根源去消滅問(wèn)題,至于解決問(wèn)題的方法,一千個(gè)人眼中一千個(gè)哈姆雷特,你喜歡哪種,就用那種方法來(lái)。
文章來(lái)源:“H5案例分享”團(tuán)隊(duì)原創(chuàng)文章
0 條評(píng)論