移動端點擊、觸碰

這篇文章將會闡述如下問題

  1. 延遲的clickhtml

  2. 擁抱tapnode

  3. 一次觸碰android

  4. 阻止它們!!!preventDefault仍是stopPropagtiongit

  5. 模擬事件是什麼鬼web

事故現場編程

  1. 延遲bootstrap

  2. 點穿(包含angular的ng-click)瀏覽器

  3. 焦點獲取iphone

分析Yocto,zepto,fastclick帶來的思考組件化

  1. zepto -- 萬惡的tap

  2. fastclick -- 「完美」

  3. Yocto -- 「後zepto時代替代者」

讓咱們開始吧!

遲到的click

「移動端最好用tap,click是有延遲的...」

開始寫移動端事件的時候就被告知了這一句真理,一直也沒放在心上,tap就tap吧。 可是忽然有一天很無聊,想要看看,到底爲何click有延遲,延遲多少。

咱們先來看兩張圖

移動端點擊、觸碰隨記

移動端點擊、觸碰隨記

*這裏的數據可能不許,由於屢次點擊的時間會不一樣,可是大概也就這樣-_-# 這裏注意到

1. click的時間基本固定在301ms

2. tap的時間會有點飄忽,差很少70-150ms左右。

爲何要給出這樣的數據呢,是爲了印證click確實是比tap慢的。網上基本比較一致的緣由是:

"iphone建立了雙擊縮放的標準,背後的實現是,在第一次的點擊時等待300ms,肯定下一次是否繼續點擊。而後被衆多移動端瀏覽器商家紛紛模仿 -----我亂說的"

這就是click有300ms延遲的緣由

既然咱們知道了是因爲縮放引發的延遲,那麼咱們加入熟悉的

<meta name="viewport" content="width=device-width user-scalable=no">

再看一下數據,發現延遲果真沒有了,速度和tap一毛同樣(真是睜着眼說瞎話啊...)。

移動端點擊、觸碰隨記

然而user-scalble=no 是很是邪惡的東西,咱們不能光靠這個去解決問題,所以咱們須要擁抱新同窗tap...

新同窗tap

tap事件運用最多莫過於zepto啦。看一片zepto的源碼好了。

移動端點擊、觸碰隨記

這片代碼位於zepto.js最底部,由於篇幅縮短了swiper和dbclick的判斷,可是能夠看出,tap是基於touchstart和touchend模擬的(應該說別無他法)。

這就是tap比click快的緣由

一次觸碰

因爲歷史緣由,所以在移動端進行觸碰的時候,也會觸發鼠標的事件。這裏給出測試結果。 觸發的順序是:

  • touchstart

  • touchmove

  • touchend

  • mouseover

  • mouseenter

  • mousedown

  • click

整個觸碰過程當中,從touchstart開始,到click終結。

能夠看出tap和click的本質上其實只是觸碰的不一樣階段。

阻止它們!preventDefault仍是stopPropagation

既然zepto能夠本身建立一個tap事件,咱們是否是也能夠呢。考慮再三,難點在於怎麼將上面控制一次觸碰的流程,因爲不知道這些事件是具備上下級(是否冒泡) 仍是 傳遞性質。 (後來發現本身腦洞略大,爲何會這麼想呢...TAT)

在web端,preventDefault最經常使用的場景莫過於 a連接submit按鈕 。 由於preventDefault的定義是

「取消事件的默認行爲。若是cancelable是true,則可使用這個方法。」

而stopPropagation的定義是

「取消事件的進一步捕獲或冒泡,若是bubbles爲true,則可使用這個方法。」

那麼在移動端,若是想要改變一次觸碰的的流程,究竟是阻止默認行爲,仍是冒泡呢。

這裏上一個demo的截圖

移動端點擊、觸碰隨記

通過測試,發現stopPropagation並不能阻止click的觸發,而preventDefault被證明在確實是無解的大招

這裏插入一個知識點:stopImmediatePropagation是一個很屌的方法,不只可以阻止事件冒泡,並且可以阻止綁定在同一個[事件名稱]上的全部後續事件觸發。

stopImmeadiatePropagation的demo傳送門

阻止移動端的觸碰默認行爲。preventDefault是有着絕對的做用的。

模擬事件是什麼鬼

在web端模擬一個事件已是很成熟啦,好處一堆,便於測試,分離代碼,組件化等等...咱們是否可以應用在移動端呢。 先看到模擬事件分爲如下四種

  • UIEvents

  • MouseEvents

  • MutationEvents

  • HTMLEvents

流程大概分爲

  1. 建立Event

  2. 初始化Event

  3. 觸發Event

觸發以後就和正常流程觸發的事件別無二致啦。這裏給出一個<<高級編程>>的典型點擊demo。後面咱們要用到這個demo去作一些很酷的事情!

移動端點擊、觸碰隨記

事故現場

延遲

前面說過了,點擊延遲主要由於不肯定是否進行縮放致使的,可是user-scalable並非什麼靈丹妙藥,因而咱們擁抱了tap,用touch-starttouch-end模擬了一個輕觸tap事件,使得點擊看起來快速了不少。典型表明就是zepto的tap事件。

解決方案:

假如應用場景不復雜(沒有滑動,獲取焦點),咱們能夠很是簡單模擬一個閹割版的點擊,並且也同時避免了點穿。如圖

移動端點擊、觸碰隨記

點穿

大部分用zepto的同窗必定踩過這個坑。

「zepto竟然把事件都綁定在document上!這太坑了。」

呵呵,反正踩完坑就開始一頓亂噴,可是問題仍是沒有解決,若是要簡單解決單純的點擊和上面同樣,加上霸道的e.preventDefault()阻止默認事件傳遞就行了。可是咱們用zepto還用了swiper,閹割版的作法還會致使一系列問題。因而乎號稱完美的fastclick閃亮登場了。

可是同樣是把事件綁定在document上,爲何fastclick就不坑了呢。

呵呵。

解決方案:

  1. fastclick (FT Labs,目前GitHub已經10000星星了.....)

  2. Yocto(基於zepto的支付寶移動端庫)

  3. 模擬fastclick作法,加入到本身豪華午飯裏^_^

然而

在使用angular的時候,無可避免必定會用到ng-click

(誰說無可避免,不是還有ng-touch這樣的類庫嗎 逃...)

那麼不用類庫,直接了當解決ng-click點穿怎麼是好。

然而-解決方案:

古語有云:用angular的都是屌絲,屌絲通常都用了user-scalable=no,因此click速度不擔憂...

只須要在ng-click=fn($event),傳入event就又可使用大招event.preventDefault

然而更合理的作法應該是這樣的。

移動端點擊、觸碰隨記

傳送門:關於angular自定義touchstart無效的解決辦法

事已至此,點穿就解決了。(啦啦啦啦啦啦啦)

焦點獲取

點穿以後,無非就是焦點獲取錯誤或者觸發了其餘表單元素,因此咱們也必須處理一下這些邊角料。

這裏列一下坑

  • textarea 須要focus

  • selct (這個天坑啊 android須要click IOS須要focus)

  • input (file,image,radio,checkbox) 須要click

  • label (*通常不操做,可是友好的作法是聚焦到對應的input)

所以作法是,設置一個標誌量needFocus,在觸碰的過程當中判斷target是否爲須要focus的,是就取消點擊,改成聚焦

一、獲取target類型

target.nodeName.toLowerCase();二、聚焦

target.focus()三、聚焦在label對應的input

document.getElmentById(labelElement.htmlFor).focus()

fastclick

這裏簡單說一下fastclick的解決點穿的原理

  1. fastclick綁定在document.body上,檢查一次觸碰的全部事件

  2. 用touchstart-touchend模擬了tap,而是在阻止真實的click觸發後,模擬了真實click點擊對象上。從而快速又完整的完成一次點擊,而且不點穿。

  3. fastclick依然對雙擊事件作了保留,作法是,在touchend判斷是否處於上次點擊的時間範圍內(fastclick出的是200ms)

  4. fastclick沒有放棄swipe,中間加入了touchmove,使用了移動的範圍(fastclick給出的是10)來判斷是否進行滑動。

  5. fastclick對labelselect作了很是細緻的處理(萬星項目呢,開玩笑呢!)

上面說的很複雜,僞代碼表示一下

var lastClickTime,trackClick; // 上一次點擊時間,和是否追蹤點擊btn.bind("touchstart",function(e){

// 判斷是否用戶快速雙擊事件,若是是,禁止本次觸碰後續操做(包括模擬點擊事件) trackClick = true;

if(e.timeStamp - lastClickTime < 200){

e.preventDefault();

} },false);btn.bind("touchmove",function(e){

// 判斷是否移動過大致使偏移原標 或者 移動超出必定範圍(這裏是) if(moveToAnotherTarget || moveTooMuch){

trackClick = false;

}},false);btn.bind("touchend",function(e){

// 依然進行是否[追蹤]和[雙擊]判斷,這裏省略...

// 這裏判斷進行[焦點]仍是[下拉菜單]仍是須要[點擊] // 這裏按照點擊來進行,先禁用原生的點擊,再把模擬的點擊事件發送給當前目標 e.preventDefault();

// 這裏就是模擬事件發揮的地方了 e.target.dispatch(mouseClickEvent);},false);

其實fastclick的核心就是[e.preventDefault阻止真實click][分發模擬鼠標事件的click]。#我亂說的#

Yocto

雖然名爲支付寶下的[超輕]移動類庫,這裏仍是想像成一個維護快速的zepto會比較好。 作法和fastclick同樣。看一下他們更新的issue#

移動端點擊、觸碰隨記

那既然有fastclick,爲何要看Yocto呢。

一、 zepto+fastclick顯得很不必二、 [輕]能夠獨立引用yocto-event.js三、 基於zepto,學習成本低

傳送門:yocto對於修改zepto的最佳實踐

目前這個項目的gitlab外人時看不到的,因此只能等待他們徹底開源了,以前看無線的移動端優化建議曾經提到,所以留心了一下,出來以後又能夠猛偷了...(讀書人的事情怎麼能叫偷 逃...)

到這裏,差很少恰好20分鐘,文章也結束了。

ps:FastClick源碼比較短,這裏簡單作個引讀好了,但願不要誤人子弟(掩面...

fastclick = {

標誌變量, // 一堆輔助判斷觸碰類型的變量

核心方法:{ // 這些就是核心精華啊...10000個星星都在這裏了 onTouchStart

onTouchMove

onTouchEnd

onClick

onMouse

focus }

用戶定義方法:{

needClick, // 用戶決定最終行爲 needFocus // 用戶決定最終行爲 }

難點:{

updateScrollParent,// 對滑動進行了細緻的兼容,實現比較複雜 stopImmediate方法 // 對不兼容此方法的環境改變了事件定義方式 }}

參考文章:

移動端頁面touch會穿透,這是bug麼?

yocto的點穿demo

完全解決tap「點透」,提高移動端點擊響應速度

 

用bootstrap的同窗,手機點擊問題:很多朋友反饋在手機瀏覽器中沒法點擊下拉菜單的連接,搜索了一番,打開 bootstrap.min.js,查找到 ontouchstart ,替換爲 disable-ontouchstart 就能夠了。

相關文章
相關標籤/搜索