移動web開發(fā)技能之touch事件詳解
概述
單擊事件是任何一個前端頁面中最常用的交互行為之一,在傳統(tǒng)的PC端大部分是使用click事件來實現(xiàn)用戶單擊交互的程序邏輯,而在移動Web端新增了touch事件來實現(xiàn)移動端更加敏感和復(fù)雜的觸摸交互行為。本章將就移動端touch事件的使用以及它與PC端的click事件的區(qū)別進行深入探討。
touch事件
在傳統(tǒng)的PC端,用戶的單擊操作主要是由鼠標(biāo)的左鍵或者右鍵來產(chǎn)生,它主要是指鼠標(biāo)的按鈕被按下,并且在很短的時間內(nèi)(一般小于300ms)又被釋放開,這就被稱為單擊操作(或稱為一次點擊操作)。
而對于移動Web端,同樣也是如此,當(dāng)手指觸摸到屏幕時開始計算時間,并且在300ms內(nèi)離開屏幕,這段時間手指不能移動,這就算是移動Web端的單擊事件,手指觸摸就被稱為touch。
touch事件分類
移動Web端的touch觸摸事件主要由屏幕和觸摸點組成,其中屏幕可以是手機、平板或者觸摸板,而觸摸點可以通過手指、胳膊肘或觸摸筆,甚至耳朵、鼻子都行,但一般是通過手指。根據(jù)touch觸摸的類型可分為以下4種事件:
- touchstart:當(dāng)手指與屏幕接觸時觸發(fā)。
- touchmove:當(dāng)手指在屏幕上滑動時連續(xù)地觸發(fā)。
- touchend:當(dāng)手指從屏幕上離開時觸發(fā)。
- touchcancel:當(dāng)touch事件被迫終止,例如電話接入或者彈出信息時會觸發(fā),或者當(dāng)觸摸點太多,超過了支持的上限(自動取消早先的觸摸點)時觸發(fā),一般不常用。
相比PC端,以上4種事件將用戶的touch行為劃分得更細,并且通過這些細化的事件可以實現(xiàn)移動Web端獨有的用戶交互行為,例如拖動swipe、長按longtap、雙指縮放pinch,等等。
其中的touchstart、touchmove和touchend是最常用的3個事件,其中touchstart最先觸發(fā),touchend結(jié)束時觸發(fā),而touchmove是否觸發(fā)取決于手指是否在觸摸屏上移動。下面用代碼來感受一下這3種事件的觸發(fā)順序,如下
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> <title>touch事件</title> <script type="text/javascript"> document.addEventListener("touchstart",function () { console.log("開始觸摸"); }); document.addEventListener("touchmove",function () { console.log("移動手指"); }); document.addEventListener("touchend",function () { console.log("結(jié)束觸摸") }); </script> </head> <body> </body> </html>
在瀏覽器中運行這段代碼,同時注意要啟用Chrome中DevTools工具中的Device Mode功能,并使用鼠標(biāo)模擬手指在屏幕上觸發(fā)觸摸事件,隨后就會在Console控制臺看到打印出對應(yīng)的日志,從中可以看到一個簡單的觸摸操作是如何完成的。
touch事件對象
對于touch事件,每一次觸發(fā)都可以得到一個事件對象,在JavaScript中這個對象叫作TouchEvent,利用TouchEvent可以獲取touch事件觸發(fā)時的坐標(biāo)、元素以及到底有幾個手指觸發(fā)等,下面就來了解一下TouchEvent事件對象。
可以在Console控制臺打印出當(dāng)前觸發(fā)touch時的TouchEvent對象,代碼如下所示:
document.addEventListener("touchstart",function (e) { console.log(e); });
打印的內(nèi)容如圖:
在上面的TouchEvent的屬性中,經(jīng)常使用的就是touches、targetTouches和changedTouches,它們的含義分別是:
- touches:當(dāng)前頁面(屏幕)上所有的觸摸點。
- targetTouches:當(dāng)前綁定事件的元素上的觸摸點。
- changedTouches:當(dāng)前屏幕上剛剛接觸的手指或者離開的手指的觸摸點。
這3個屬性返回的是TouchList對象,代表的是一個touch的集合數(shù)組,也就是說每一次touch觸發(fā),都會兼顧到多指觸摸的場景,下面就分別以單指觸摸的場景和多指觸摸的場景來講解這3個屬性的區(qū)別。
首先是單指觸摸的場景,我們來模擬用戶一個手指觸摸,如圖。
外層的線框代表頁面,里面的一個<div>
元素綁定了touch事件,1號手指觸摸了該<div>
元素,這時touches、targetTouches以及changedTouches里面的觸摸點都是指1號手指這個觸摸點,應(yīng)該很好理解。 對于多指觸摸的場景,條件是手指觸摸屏幕之后暫不離開,如圖。
外層的線框代表頁面,里面的一個<div>
元素綁定了touch事件,首先1號手指第一個觸摸了該<div>
元素,然后2號手指第二個也觸摸了該<div>
元素,最后3號手指第三個觸摸了div外面的區(qū)域,這時touches涵蓋的觸摸點的集合數(shù)組包括1號、2號、3號手指,而targetTouches涵蓋的觸摸點的集合數(shù)組包括1號和2號手指,而changedTouches涵蓋的觸摸點的集合數(shù)組包括2號和3號手指。
當(dāng)手指都離開屏幕之后,touches和targetTouches中將不會再有值,changedTouches還會有一個值,此值為最后一個離開屏幕的手指的接觸點。這就是touches、targetTouches和changedTouches這3個屬性對于單指觸摸的場景和多指觸摸的場景下的區(qū)別,總結(jié)如下:
單指觸摸的場景:
- touches:1號手指
- targetTouches:1號手指
- changedTouches:1號手指
多指觸摸的場景:
- touches:1,2,3號手指
- targetTouches:1,2號手指
- changedTouches:2,3號手指
對于單指觸摸的場景來說,它們并無區(qū)別,主要區(qū)別在于多指觸摸的場景,所以在使用時可以根據(jù)具體的程序邏輯來選擇使用合適的屬性。
對于涵蓋觸摸點的集合數(shù)組TouchList而言,里面每個元素都是一個touch對象,通過這個對象可以獲取當(dāng)前觸摸的位置,如圖。
其中,主要用到了offsetX/Y、pageX/Y和clientX/Y這3個屬性,它們的區(qū)別和含義分別是:
- offsetX/Y:觸摸位置相當(dāng)于事件源元素的位置坐標(biāo),以當(dāng)前
<div>
元素盒子模型的內(nèi)容區(qū)域的左上角為原點。 - pageX/Y:觸摸位置相當(dāng)于整個頁面內(nèi)容區(qū)域的位置坐標(biāo),當(dāng)頁面過長時,包括滾動隱藏的部分內(nèi)容,以頁面完整內(nèi)容區(qū)域的左上角為原點。
- clientX/Y:觸摸位置相當(dāng)于瀏覽器視區(qū)(屏幕)區(qū)域的位置坐標(biāo),以相對于頁面的可見部分內(nèi)容區(qū)域的左上角為原點。
具體的位置和距離可以參考下圖,外層表示頁面的所有內(nèi)容,中間框表示瀏覽器的視區(qū),其中有一個<div>
元素綁定了touch事件,黑點表示觸摸點的位置。
移動web單擊事件
在了解了touch事件之后,我們知道移動Web端的單擊事件完全可以由touchstart、touchmove和touchend來組合實現(xiàn),移動Web端同時也提供了原生的click事件,它和傳統(tǒng)的PC端的click事件一樣,在用戶完成一次完整的手指單擊屏幕之后觸發(fā)。在移動Web端使用click綁定單擊事件,代碼如下:
document.addEventListener("click",function (e) { console.log(e); });
一切看似都很順利,在需要使用單擊時就用click事件,在需要使用touch時(拖動,長按等)就使用touch對應(yīng)的事件。但是,對于移動Web端而言,處于iOS系統(tǒng)或Android系統(tǒng)時,采用click實現(xiàn)單擊事件卻有著不同的表現(xiàn)。
iOS單擊延遲
這要追溯至2007年初,蘋果公司在發(fā)布首款iPhone前遇到了一個問題:當(dāng)時的網(wǎng)站都是為大屏幕設(shè)備所設(shè)計的,于是提出了視區(qū)(Viewport)的概念,其中一項即是用戶在瀏覽網(wǎng)頁時,可以在頁面的任何地方通過雙擊操作將頁面放大(Double Tap to Zoom)。這個交互功能提升了用戶瀏覽網(wǎng)頁時的體驗,于是Android和iOS的移動端瀏覽器紛紛支持了這個功能,但是對于雙擊這個操作而言,其實是包括了兩次單擊操作,當(dāng)?shù)谝淮螁螕敉瓿珊螅到y(tǒng)需要有一段時間來監(jiān)聽是否有第二次單擊,如果有則表明此次操作是一個雙擊操作,而這段時間間隔大概有300毫秒(ms)。
因此,哪怕是只想要單擊這個事件,也都會經(jīng)過雙擊放大這個判斷邏輯,導(dǎo)致要等到300毫秒之后才能收到單擊事件程序邏輯的反饋,這就是300毫秒的單擊延遲問題。
對于Android系統(tǒng)的瀏覽器而言,可以通過給視區(qū)設(shè)置user-scalable=no來禁止用戶進行縮放,隨后就可以正常地使用原生的click事件而沒有延遲;對于iOS系統(tǒng)而言,瀏覽器對user-scalable支持度存在Bug(漏洞),導(dǎo)致了無法通過簡單的設(shè)置來達到正常使用原生click事件的目的。代碼如下:
<meta name="viewport" content=" initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
所以,在iOS移動端,如果想要實現(xiàn)真正的單擊事件而沒有300毫秒延遲問題,就不能采用原生的click事件,可以通過touch(touchstart、touchmove和touchend)事件來模擬一次單擊操作。好在當(dāng)前業(yè)界已有比較流行的方案,例如Zepto.js中的tap事件和FastClick.js庫可用來解決這個問題,在這里主要介紹一下FastClick.js庫。
FastClick.js是FT Labs團隊結(jié)合touch事件專門為解決移動端瀏覽器的300毫秒單擊延遲問題所開發(fā)的一個輕量級的庫。正常情況下,在移動Web端,當(dāng)用戶單擊屏幕時,會依次觸發(fā)touchstart、touchmove(0 次或多次)、touchend、click(原生)這些事件。touchmove事件只有當(dāng)手指在屏幕上移動時才會觸發(fā)。Touchstart、touchmove或者touchend 事件的任意一個調(diào)用event.preventDefault()方法,都會直接阻止原生click事件的觸發(fā)。
FastClick的實現(xiàn)原理是在檢測到touchend事件觸發(fā)時,把瀏覽器在300毫秒之后原生的click事件阻止掉,然后通過DOM自定義事件立即發(fā)出一個模擬的click事件,這樣就消除了300毫秒的延遲,提供了一個快速響應(yīng)的“單擊”事件。如下代碼演示了FastClick的使用。
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> <title>FastClick.js</title> <script type="text/javascript" src="./fastclick.js"></script> </head> <body> <button id="click">點我</button> <script type="text/javascript"> // 頁面加載完成后,使用FastClick,一般傳遞最外層的body元素即可 document.addEventListener('DOMContentLoaded', function(){ FastClick.attach(document.body);// 在實際的項目中,需判斷在iOS移動端才需要此程序邏輯 }, false); document.getElementById("click").addEventListener("click",function(){ alert("單擊觸發(fā)! "); },false) </script> </body> </html>
需要注意的是,在不修改<meta>
標(biāo)簽中的user-scalable屬性的情況下,300毫秒單擊延遲的問題只會出現(xiàn)在iOS系統(tǒng)的瀏覽器中,并且解決方案只需要針對iOS端,上文也提到了這個問題的產(chǎn)生是由于對user-scalable支持度存在Bug,之后蘋果公司也意識到了這個問題的嚴重性,于是在iOS 9.3版本時,提供了一個基于新的內(nèi)核WKWebView的瀏覽器,并將其應(yīng)用在Safari瀏覽器上,由此解決了這個問題(存在300毫秒單擊延遲問題的瀏覽器是UIWebView,這個內(nèi)核已經(jīng)不再維護了),并且后續(xù)使用iOS 9.3版本系統(tǒng)的瀏覽器在訪問頁面時,會默認使用WKWebView瀏覽器。
至此,移動Web端的300毫秒單擊延遲問題得到了徹底的改善。
“單擊穿透”問題
在移動Web端,有一個很常見的應(yīng)用場景,單擊一個按鈕會出現(xiàn)一個蒙層,此蒙層是全屏遮蓋,并且有最高層級,當(dāng)單擊蒙層時,蒙層消失。此場景和交互操作看似并沒有什么問題,但是假如頁面中有一個綁定了單擊事件的<div>
元素被蒙層遮蓋,而單擊蒙層關(guān)閉時的位置剛好和該<div>
元素重合,那么蒙層關(guān)閉后會同時觸發(fā)該<div>
元素的單擊事件,對于用戶來說,這個操作并不是要單擊該<div>
元素,這就是所謂的“單擊穿透”問題,如圖。
出現(xiàn)“單擊穿透”問題需要有個條件,即蒙層是通過綁定的touch事件來實現(xiàn)隱藏,而其遮蓋的<div>
元素綁定的是原生click事件,這樣就形成了touch事件觸發(fā)之后,蒙層隱藏了,300毫秒后當(dāng)前這個觸摸點的click事件又觸發(fā)了,就形成“單擊穿透”。
移動Web端的“單擊穿透”問題出現(xiàn)的原因其實和300毫秒單擊延遲問題脫不了關(guān)系,但是“單擊穿透”出現(xiàn)的場景比較單一,并且也比較好解決。
解決“單擊穿透”問題可以從問題出現(xiàn)的原因上來著手,主要有以下兩種解決方案:
- 不要同時混用touch事件和click事件,要么給蒙層和
<div>
元素同時綁定touch事件,要么同時綁定click事件,在iOS 9.3版本之后,只用click事件即可,此方案體驗最好。 - 延遲蒙層消失的時間,例如在touch事件觸發(fā)后,在350毫秒后再讓蒙層消失,這樣后面的
<div>
元素就不會觸發(fā)click事件了,此方案會導(dǎo)致蒙層消失的響應(yīng)慢,體驗差,并且有時會觸發(fā)兩次消失邏輯,故不推薦使用。
無論是300毫秒單擊延遲問題,還是“單擊穿透”問題,這些都是移動Web端特有的問題,也在一定程度上反映出移動Web端環(huán)境的復(fù)雜性,需要注意支持度和兼容性問題的地方很多,所以大家在進行移動Web端開發(fā)時,要有意識地去關(guān)注這些問題。
以上就是移動web開發(fā)技能之touch事件詳解的詳細內(nèi)容,更多關(guān)于web移動touch事件的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Drawer?Builder組件實現(xiàn)flutter側(cè)邊抽屜效果示例分析
這篇文章主要為大家介紹了Drawer?Builder組件實現(xiàn)flutter側(cè)邊抽屜效果示例分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-10-10iOS 實現(xiàn)簡單的加載等待動畫示例(思路與實現(xiàn))
本篇文章主要介紹了iOS 實現(xiàn)簡單的加載等待動畫示例(思路與實現(xiàn)),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-05-05