摘要:所有瀏覽器瀏覽器不支持安卓中中有屬性安卓中中有屬性有屬性的有屬性的所以在不需要的瀏覽器會(huì)直接掉,不會(huì)執(zhí)行下面的所有代碼。見源碼行,可以看出在響應(yīng)無操作后,則觸發(fā)。
其實(shí)一直就想花些時(shí)間讀一讀那些優(yōu)秀的開源庫,今天終于下了決定打算死磕下自己,2016年每個(gè)月讀2-3個(gè)優(yōu)秀的開源庫,把源碼精彩的地方和自己心得分享給大家。
目錄(一)背景(一)背景
(二)源碼解析
(三)Zepto 點(diǎn)擊穿透與 FastClick
(四)新技能 Get
(五)參考文獻(xiàn)
做前端的一定都知道,原生click事件在移動(dòng)瀏覽器上會(huì)有300毫秒的延遲,會(huì)讓用戶覺得卡頓,這300毫秒到底是怎么來的呢,估計(jì)就不太知道了,再繼續(xù)深究這300毫秒的來源是什么,應(yīng)該會(huì)更少人知道吧。國外有一篇很有名的文章說的很詳細(xì),有興趣可以看一下:what-exactly-is.....-the-300ms-click-delay。簡短來說:是移動(dòng)瀏覽器都支持雙擊縮放或雙擊滾動(dòng)的操作,由于當(dāng)用戶第一次點(diǎn)擊屏幕后,瀏覽器不能立刻判斷用戶確實(shí)要打開這個(gè)鏈接,還是想要進(jìn)行雙擊的操作,因此幾乎現(xiàn)在所有瀏覽器都效仿Safari當(dāng)年的約定,在點(diǎn)擊事件上加了300毫秒的延遲。
在這個(gè)web頁面橫行的年代,每個(gè)點(diǎn)擊事件都有300毫秒的延遲是不允許的。再后來出來了很多的解決辦法,比如Zepto的tap事件(會(huì)引發(fā)擊穿的bug,后面會(huì)著重說),fastclick.js等都可以解決,但是多多少少會(huì)有些負(fù)作用,綜合起來我最喜歡fastclick的解決方案,今天就來讀一讀它的源碼吧~
(二)解析 1. 引入 FastClick 到自己的環(huán)境829行:現(xiàn)在一般插件都會(huì)用這種方式兼容AMD、commonJs風(fēng)格、原生Js的方式。還有CMD等這里沒有兼容,這里可以根據(jù)自己項(xiàng)目需求稍作修改。
//優(yōu)先兼容AMD方式 if (typeof define === "function" && typeof define.amd === "object" && define.amd) { define(function() { return FastClick; }); } else if (typeof module !== "undefined" && module.exports) { //兼容commonJs風(fēng)格 module.exports = FastClick.attach; module.exports.FastClick = FastClick; } else { //最后兼容原生Js window.FastClick = FastClick; }2. 入口
824行:FastClick入口方法attach
//layer參數(shù):要監(jiān)聽的dom對象,一般是document.body //options參數(shù):用來覆蓋自定義參數(shù),個(gè)人建議不去覆蓋, //因?yàn)槔锩娴膮?shù)設(shè)定都是FastClick的精華, //比如規(guī)定了touchstart和touchend事件之間的200毫秒最小間隔。 FastClick.attach = function(layer, options) { return new FastClick(layer, options); };3. FastClick 函數(shù) 1. 23-103行:設(shè)置默認(rèn)值
//比如這幾個(gè)參數(shù),上面提到不建議自定義覆蓋, //這些參數(shù)正是FastClick的精華所在, //大幅度修改數(shù)值可能讓整個(gè)庫的功效大打折扣。 this.touchBoundary = options.touchBoundary || 10; this.tapDelay = options.tapDelay || 200; this.tapTimeout = options.tapTimeout || 700;2. 105-107行:判斷是否需要調(diào)用FastClick
官網(wǎng)上when-it-isnt-needed說的很清楚,以下情況不需要FastClick。
所有pc瀏覽器
瀏覽器不支持ontouchstart
安卓中chrome(all versions)meta中有user-scalable="no"屬性
安卓中chrome 32+ meta中有width=device-width 屬性
BlackBerry 10.3+
Firefox 27+
有-ms-touch-action: manipulation屬性的IE10
有touch-action: manipulation屬性的IE11
//所以在不需要FastClick的瀏覽器會(huì)直接return掉, //不會(huì)執(zhí)行下面的所有代碼。 if (FastClick.notNeeded(layer)) { return; }3. 110-132行:自定義函數(shù)綁定在對應(yīng)默認(rèn)事件上
layer.addEventListener("touchstart", this.onTouchStart, false); layer.addEventListener("touchmove", this.onTouchMove, false); layer.addEventListener("touchend", this.onTouchEnd, false); layer.addEventListener("touchcancel", this.onTouchCancel, false);4. 137-159行:對舊版本android不支持 stopImmediatePropagation 事件的兼容
這里有一個(gè)知識(shí)點(diǎn):stopImmediatePropagation和stopPropagation的區(qū)別,后面總結(jié)會(huì)詳細(xì)說。
5. 164-173行:兼容直接綁定在dom上的onclick事件//把 直接綁定在dom上的onclick事件 //改為綁定在該dom上的形式, //為了之后的模擬點(diǎn)擊事件。 if (typeof layer.onclick === "function") { oldOnClick = layer.onclick; layer.addEventListener("click", function(event) { oldOnClick(event); }, false); layer.onclick = null; }4. 兼容 & 判斷
181-219行:瀏覽器UA判斷
311-319行:determineEventType 兼容安卓chrome中的select框事件從click改為mousedown
325-355行:focus 兼容蘋果手機(jī)setSelectionRange不能正確獲取焦點(diǎn)的bug
343-367行:updateScrollParent (待看)
374-382行:getTargetElementFromEventTarget 兼容獲取點(diǎn)擊元素,iOS 4.1中會(huì)獲取文字作為焦點(diǎn),取它的父元素dom
497-512行:findControl
//點(diǎn)擊label的時(shí)候,找到他對應(yīng)的元素,并獲取焦點(diǎn)
459-467:touchHasMoved 手指點(diǎn)擊時(shí)移動(dòng)間距大于10px,返回true
476-488:onTouchMove 手指點(diǎn)擊時(shí)移動(dòng)間距大于10px,即視為touchmove,不觸發(fā)模擬click事件
一般情況下用不到,以下方法,特殊需求可能會(huì)用到。
227-254行:needsClick 確定哪些元素需要原生的click事件
263-285行:needsFocus 確定哪些元素需要原生的focus事件
//如果哪些元素需要使用原生的click或者是focus事件,需要在dom上加上class="needsClick" Ignored by FastClick
712-726行:destroy 這個(gè)方法只在源碼中,如果有需求銷毀事件,重構(gòu)源碼時(shí)可以調(diào)用這個(gè)方法。
6. 核心方法391-450:onTouchStart
FastClick.prototype.onTouchStart = function(event) { //tapDelay默認(rèn)300毫秒,點(diǎn)擊時(shí)間差小于300毫秒,則阻止事件再次觸發(fā),阻止短時(shí)間內(nèi)雙擊的問題 if ((event.timeStamp - this.lastClickTime) < this.tapDelay) { event.preventDefault(); } }
521-610:onTouchEnd
if (!this.needsClick(targetElement)) { // 如果這不是一個(gè)需要使用原生click的元素,則屏蔽原生事件,避免觸發(fā)兩次click event.preventDefault(); // 觸發(fā)一次模擬的click this.sendClick(targetElement, event); }
294-309:sendClick(核心方法)
//這個(gè)事件會(huì)在onTouchEnd中用到,經(jīng)過一系列的判斷,符合條件,調(diào)用這個(gè)模擬事件 FastClick.prototype.sendClick = function(targetElement, event) { var clickEvent, touch; //創(chuàng)建一個(gè)鼠標(biāo)事件 clickEvent = document.createEvent("MouseEvents"); //初始化鼠標(biāo)事件 clickEvent.initMouseEvent(this.determineEventType(targetElement), true, true, window, 1, touch.screenX, touch.screenY, touch.clientX, touch.clientY, false, false, false, false, 0, null); //觸發(fā)這個(gè)事件 targetElement.dispatchEvent(clickEvent); };(三)Zepto 點(diǎn)擊穿透與 FastClick
最近項(xiàng)目中在用Zepto的插件touch.js中tap事件,來解決移動(dòng)瀏覽器中300毫秒延遲的問題。但是出現(xiàn)了各種擊穿現(xiàn)象
同頁面tap點(diǎn)擊彈出彈層,彈層中也有一個(gè)button,正好重疊的時(shí)候,會(huì)出現(xiàn)擊穿
tap事件點(diǎn)擊,頁面跳轉(zhuǎn),新頁面中同位置也有一個(gè)按鈕,會(huì)出現(xiàn)擊穿
我們可以看下Zepto對 singleTap 事件的處理。見源碼 136-143 行,可以看出在 touchend 響應(yīng) 250ms 無操作后,則觸發(fā)singleTap。
//trigger single tap after 250ms of inactivity else { touchTimeout = setTimeout(function(){ touchTimeout = null if (touch.el) touch.el.trigger("singleTap") touch = {} }, 250) }
用這篇文章里面的一句話來解釋下Zepto的穿透問題也來說說touch事件與點(diǎn)擊穿透問題
zepto中的 tap 通過兼聽綁定在 document 上的 touch 事件來完成 tap 事件的模擬的,是通過事件冒泡實(shí)現(xiàn)的。在點(diǎn)擊完成時(shí)(touchstart / touchend)的 tap 事件需要冒泡到 document 上才會(huì)觸發(fā)。而在冒泡到 document 之前,手指接觸和離開屏幕(touchstart / touchend)是會(huì)觸發(fā) click 事件的。
因?yàn)?click 事件有延遲(大概是300ms,為了實(shí)現(xiàn)safari的雙擊事件的設(shè)計(jì)),所以在執(zhí)行完 tap 事件之后,彈出層立馬就隱藏了,此時(shí) click 事件還在延遲的 300ms 之中。當(dāng) 300ms 到來的時(shí)候,click 到的其實(shí)是隱藏元素下方的元素。
如果正下方的元素有綁定 click 事件,此時(shí)便會(huì)觸發(fā),如果沒有綁定 click 事件的話就當(dāng)沒發(fā)生。如果正下方的是 input 輸入框(或是 select / radio / checkbox),點(diǎn)擊默認(rèn) focus 而彈出輸入鍵盤,也就出現(xiàn)了上面的“點(diǎn)透”現(xiàn)象。
所以到這里,個(gè)人還是建議直接使用fastclick.js庫來解決移動(dòng)端瀏覽器300毫秒的問題,不建議自己寫,坑還是挺多的,這個(gè)庫壓縮后還是挺小的,可以用各種方式引用,來替代Zepto中的touch.js插件是個(gè)不錯(cuò)的辦法。
(四)新技能 Get通過讀這個(gè)庫,發(fā)現(xiàn)了很多知識(shí)上的盲區(qū)或者理解的并不是很透徹的點(diǎn),再深化一下~
stopImmediatePropagation 和 stopPropagation 的區(qū)別 參考文章
他們都可以阻止事件冒泡到父元素
stopImmediatePropagation多做了一件事:阻止綁定在該元素上其他事件運(yùn)行
(五)參考文獻(xiàn)300毫秒的起源:what-exactly-is.....-the-300ms-click-delay
stopImmediatePropagation 和 stopPropagation 的區(qū)別:http://segmentfault.com/q/1010000000120125
也來說說touch事件與點(diǎn)擊穿透問題:http://segmentfault.com/a/1190000003848737
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/78495.html
摘要:為什么安卓可以正常工作代碼在剛運(yùn)行的時(shí)候,就判斷是否需要使用,我的安卓測試機(jī)大于且設(shè)置了。所以在安卓下我點(diǎn)擊使用的原生事件當(dāng)然沒問題。在第二次手動(dòng)事件中,因?yàn)榇藭r(shí)為,所以在中返回,接著從而順利觸發(fā)了原生事件。 在昨天的一個(gè)移動(dòng)端項(xiàng)目中引入fastclick后手動(dòng)觸發(fā)click事件失敗,查看了文檔也沒有找到解決的辦法,最后通過看fastclick源碼才解決。如果不想看中間這么多文字,可以...
摘要:源碼分析不愿意下代碼的可以直接點(diǎn)這里地址首先贊一下的代碼注釋,非常全。屬性一個(gè)對象,包含了代表所有從上一次觸摸事件到此次事件過程中,狀態(tài)發(fā)生了改變的觸點(diǎn)的對象。 所謂 zepto 的 touch 其實(shí)就是指這個(gè)文件啦,可以看到區(qū)區(qū) 165 行(包括注釋)就完成了 swipe 和 tap 相關(guān)的事件實(shí)現(xiàn)。在正式開始分析源碼之前,我們先說說 touch 相關(guān)的幾個(gè)事件,因?yàn)闊o論是 tap ...
摘要:移動(dòng)端觸摸點(diǎn)擊事件優(yōu)化源碼學(xué)習(xí)最近在做一些微信移動(dòng)端的頁面,在此記錄關(guān)于移動(dòng)端觸摸和點(diǎn)擊事件的學(xué)習(xí)優(yōu)化過程,主要內(nèi)容圍繞展開。當(dāng)指針設(shè)備通常指鼠標(biāo)在元素上移動(dòng)時(shí)事件被觸發(fā)。移動(dòng)端有延遲問題,可沒有哦。 移動(dòng)端觸摸、點(diǎn)擊事件優(yōu)化(fastclick源碼學(xué)習(xí)) 最近在做一些微信移動(dòng)端的頁面,在此記錄關(guān)于移動(dòng)端觸摸和點(diǎn)擊事件的學(xué)習(xí)優(yōu)化過程,主要內(nèi)容圍繞fastclick展開。fastclic...
摘要:移動(dòng)端觸摸點(diǎn)擊事件優(yōu)化源碼學(xué)習(xí)最近在做一些微信移動(dòng)端的頁面,在此記錄關(guān)于移動(dòng)端觸摸和點(diǎn)擊事件的學(xué)習(xí)優(yōu)化過程,主要內(nèi)容圍繞展開。當(dāng)指針設(shè)備通常指鼠標(biāo)在元素上移動(dòng)時(shí)事件被觸發(fā)。移動(dòng)端有延遲問題,可沒有哦。 移動(dòng)端觸摸、點(diǎn)擊事件優(yōu)化(fastclick源碼學(xué)習(xí)) 最近在做一些微信移動(dòng)端的頁面,在此記錄關(guān)于移動(dòng)端觸摸和點(diǎn)擊事件的學(xué)習(xí)優(yōu)化過程,主要內(nèi)容圍繞fastclick展開。fastclic...
閱讀 3554·2021-11-25 09:43
閱讀 3135·2021-10-08 10:04
閱讀 1625·2019-08-26 12:20
閱讀 2053·2019-08-26 12:09
閱讀 595·2019-08-23 18:25
閱讀 3573·2019-08-23 17:54
閱讀 2322·2019-08-23 17:50
閱讀 803·2019-08-23 14:33