摘要:如果偽類的參數(shù)不可以用轉(zhuǎn)換,則參數(shù)為字符串,用正則將字符串前后的或去掉,再賦值給最后執(zhí)行回調(diào),將解釋出來(lái)的參數(shù)傳入回調(diào)函數(shù)中,將執(zhí)行結(jié)果返回。重寫(xiě)的方法,改過(guò)的調(diào)用的是方法,在回調(diào)函數(shù)中處理大部分邏輯。
Selector 模塊是對(duì) Zepto 選擇器的擴(kuò)展,使得 Zepto 選擇器也可以支持部分 CSS3 選擇器和 eq 等 Zepto 定義的選擇器。
在閱讀本篇文章之前,最好先閱讀《讀Zepto源碼之神奇的$》。
讀 Zepto 源碼系列文章已經(jīng)放到了github上,歡迎star: reading-zepto
源碼版本本文閱讀的源碼為 zepto1.2.0
GitBook《reading-zepto》
輔助方法 visiblefunction visible(elem){ elem = $(elem) return !!(elem.width() || elem.height()) && elem.css("display") !== "none" }
判斷元素是否可見(jiàn)。
可見(jiàn)的標(biāo)準(zhǔn)是元素有寬或者高,并且 display 值不為 none。
filtersvar filters = $.expr[":"] = { visible: function(){ if (visible(this)) return this }, hidden: function(){ if (!visible(this)) return this }, selected: function(){ if (this.selected) return this }, checked: function(){ if (this.checked) return this }, parent: function(){ return this.parentNode }, first: function(idx){ if (idx === 0) return this }, last: function(idx, nodes){ if (idx === nodes.length - 1) return this }, eq: function(idx, _, value){ if (idx === value) return this }, contains: function(idx, _, text){ if ($(this).text().indexOf(text) > -1) return this }, has: function(idx, _, sel){ if (zepto.qsa(this, sel).length) return this } }
定義了一系列的過(guò)濾函數(shù),返回符合條件的元素。這些過(guò)濾函數(shù)會(huì)將集合中符合條件的元素過(guò)濾出來(lái),是實(shí)現(xiàn)相關(guān)選擇器的核心。
visible: 過(guò)濾可見(jiàn)元素,匹配 el:visible 選擇器
hidden: 過(guò)濾不可見(jiàn)元素, 匹配 el:hidden 選擇器
selected: 過(guò)濾選中的元素,匹配 el:selected 選擇器
checked: 過(guò)濾勾選中的元素,匹配 el:checked 選擇器
parent: 返回至少包含一個(gè)子元素的元素,匹配 el:parent 選擇器
first: 返回第一個(gè)元素,匹配 el:first 選擇器
last: 返回最后一個(gè)元素,匹配 el:last 選擇器
eq: 返回指定索引的元素,匹配 el:eq(index) 選擇器
contains: 返回包含指定文本的元素,匹配 el:contains(text)
has: 返回匹配指定選擇器的元素,匹配 el:has(sel)
processvar filterRe = new RegExp("(.*):(w+)(?:(([^)]+)))?$s*"), function process(sel, fn) { sel = sel.replace(/=#]/g, "="#"]") var filter, arg, match = filterRe.exec(sel) if (match && match[2] in filters) { filter = filters[match[2]], arg = match[3] sel = match[1] if (arg) { var num = Number(arg) if (isNaN(num)) arg = arg.replace(/^[""]|[""]$/g, "") else arg = num } } return fn(sel, filter, arg) }
process 方法是根據(jù)參數(shù) sel,分解出選擇器、偽類名和偽類參數(shù)(如 eq 、 has 的參數(shù)),根據(jù)偽類來(lái)選擇對(duì)應(yīng)的 filter ,傳遞給回調(diào)函數(shù) fn 。
分解參數(shù)最主要靠的是 filterRe 這條正則,正則太過(guò)復(fù)雜,很難解釋,不過(guò)用正則可視化網(wǎng)站 regexper.com,可以很清晰地看到,正則分成三大組,第一組匹配的是 : 前面的選擇器,第二組匹配的是偽類名,第三組匹配的是偽類參數(shù)。
sel = sel.replace(/=#]/g, "="#"]")
這段是處理 a[href^=#] 的情況,其實(shí)就是將 # 包在 "" 里面,以符合標(biāo)準(zhǔn)的屬性選擇器,這是 Zepto 的容錯(cuò)能力。 這個(gè)選擇器不會(huì)匹配到 filters 上的過(guò)濾函數(shù),最后調(diào)用的是 querySelectorAll 方法,具體見(jiàn)《讀Zepto源碼之神奇的$》對(duì) qsa 函數(shù)的分析。
if (match && match[2] in filters) { filter = filters[match[2]], arg = match[3] sel = match[1] ... }
match[2] 也即第二組匹配的是偽類名,也是對(duì)應(yīng) filters 中的 key 值,偽類名存在于 filters 中時(shí),則將選擇器,偽類名和偽類參數(shù)存入對(duì)應(yīng)的變量。
if (arg) { var num = Number(arg) if (isNaN(num)) arg = arg.replace(/^[""]|[""]$/g, "") else arg = num }
如果偽類的參數(shù)不可以用 Number 轉(zhuǎn)換,則參數(shù)為字符串,用正則將字符串前后的 " 或 " 去掉,再賦值給 arg.
return fn(sel, filter, arg)
最后執(zhí)行回調(diào),將解釋出來(lái)的參數(shù)傳入回調(diào)函數(shù)中,將執(zhí)行結(jié)果返回。
重寫(xiě)的方法 qsavar zepto = $.zepto, oldQsa = zepto.qsa, oldMatches = zepto.matches, childRe = /^s*>/, classTag = "Zepto" + (+new Date()) zepto.qsa = function(node, selector) { return process(selector, function(sel, filter, arg){ try { var taggedParent if (!sel && filter) sel = "*" else if (childRe.test(sel)) taggedParent = $(node).addClass(classTag), sel = "."+classTag+" "+sel var nodes = oldQsa(node, sel) } catch(e) { console.error("error performing selector: %o", selector) throw e } finally { if (taggedParent) taggedParent.removeClass(classTag) } return !filter ? nodes : zepto.uniq($.map(nodes, function(n, i){ return filter.call(n, i, nodes, arg) })) }) }
改過(guò)的 qsa 調(diào)用的是 process 方法,在回調(diào)函數(shù)中處理大部分邏輯。
思路是通過(guò)選擇器獲取到所有節(jié)點(diǎn),然后再調(diào)用對(duì)應(yīng)偽類的對(duì)應(yīng)方法來(lái)過(guò)濾出符合條件的節(jié)點(diǎn)。
處理選擇器,根據(jù)選擇器獲取節(jié)點(diǎn)var taggedParent if (!sel && filter) sel = "*" else if (childRe.test(sel)) taggedParent = $(node).addClass(classTag), sel = "."+classTag+" "+sel var nodes = oldQsa(node, sel)
如果選擇器和過(guò)濾器都不存在,則將 sel 設(shè)置 * ,即獲取所有元素。
如果 >sel 形式的選擇器,查找所有子元素。
這里的做法是,向元素 node 中添加唯一的樣式名 classTag,然后用唯一樣式名和選擇器拼接成子元素選擇器。
最后調(diào)用原有的 qsa 函數(shù) oldQsa 來(lái)獲取符合選擇器的所有元素。
清理所添加的樣式if (taggedParent) taggedParent.removeClass(classTag)
如果存在 taggedParent ,則將元素上的 classTag 清理掉。
調(diào)用對(duì)應(yīng)的過(guò)濾器,過(guò)濾元素return !filter ? nodes : zepto.uniq($.map(nodes, function(n, i){ return filter.call(n, i, nodes, arg) }))
如果沒(méi)有過(guò)濾器,則將所有元素返回,如果存在過(guò)濾器,則遍歷集合,調(diào)用對(duì)應(yīng)的過(guò)濾器獲取元素,并將新集合的元素去重。
matcheszepto.matches = function(node, selector){ return process(selector, function(sel, filter, arg){ return (!sel || oldMatches(node, sel)) && (!filter || filter.call(node, null, arg) === node) }) }
matches 也是調(diào)用 process 方法,這里很巧妙地用了 || 和 && 的短路操作。
其實(shí)要做的事情就是,如果可以用 oldMatches 匹配,則使用 oldMatches 匹配的結(jié)果,否則使用過(guò)濾器過(guò)濾出來(lái)的結(jié)果。
系列文章讀Zepto源碼之代碼結(jié)構(gòu)
讀Zepto源碼之內(nèi)部方法
讀Zepto源碼之工具函數(shù)
讀Zepto源碼之神奇的$
讀Zepto源碼之集合操作
讀Zepto源碼之集合元素查找
讀Zepto源碼之操作DOM
讀Zepto源碼之樣式操作
讀Zepto源碼之屬性操作
讀Zepto源碼之Event模塊
讀Zepto源碼之IE模塊
讀Zepto源碼之Callbacks模塊
讀Zepto源碼之Deferred模塊
讀Zepto源碼之Ajax模塊
讀Zepto源碼之a(chǎn)ssets模塊
參考try-catch語(yǔ)句的“偽塊作用域”
License署名-非商業(yè)性使用-禁止演繹 4.0 國(guó)際 (CC BY-NC-ND 4.0)
最后,所有文章都會(huì)同步發(fā)送到微信公眾號(hào)上,歡迎關(guān)注,歡迎提意見(jiàn):
作者:對(duì)角另一面
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/91886.html
摘要:不支持事件冒泡帶來(lái)的直接后果是不能進(jìn)行事件委托,所以需要對(duì)和事件進(jìn)行模擬。調(diào)用函數(shù),分隔出參數(shù)的事件名和命名空間。這里判斷是否為函數(shù),即第一種傳參方式,調(diào)用函數(shù)的方法,將上下文對(duì)象作為的第一個(gè)參數(shù),如果存在,則與的參數(shù)合并。 Event 模塊是 Zepto 必備的模塊之一,由于對(duì) Event Api 不太熟,Event 對(duì)象也比較復(fù)雜,所以乍一看 Event 模塊的源碼,有點(diǎn)懵,細(xì)看下...
摘要:讀源碼系列文章已經(jīng)放到了上,歡迎源碼版本本文閱讀的源碼為改寫(xiě)原有的方法模塊改寫(xiě)了以上這些方法,這些方法在調(diào)用的時(shí)候,會(huì)為返回的結(jié)果添加的屬性,用來(lái)保存原來(lái)的集合。方法的分析可以看讀源碼之模塊。 Stack 模塊為 Zepto 添加了 addSelf 和 end 方法。 讀 Zepto 源碼系列文章已經(jīng)放到了github上,歡迎star: reading-zepto 源碼版本 本文閱讀的...
摘要:模塊基于上的事件的封裝,利用屬性,封裝出系列事件。這個(gè)判斷需要引入設(shè)備偵測(cè)模塊。然后是監(jiān)測(cè)事件,根據(jù)這三個(gè)事件,可以組合出和事件。其中變量對(duì)象和模塊中的對(duì)象的作用差不多,可以先看看讀源碼之模塊對(duì)模塊的分析。 Gesture 模塊基于 IOS 上的 Gesture 事件的封裝,利用 scale 屬性,封裝出 pinch 系列事件。 讀 Zepto 源碼系列文章已經(jīng)放到了github上,歡...
摘要:模塊處理的是表單提交。表單提交包含兩部分,一部分是格式化表單數(shù)據(jù),另一部分是觸發(fā)事件,提交表單。最終返回的結(jié)果是一個(gè)數(shù)組,每個(gè)數(shù)組項(xiàng)為包含和屬性的對(duì)象。否則手動(dòng)綁定事件,如果沒(méi)有阻止瀏覽器的默認(rèn)事件,則在第一個(gè)表單上觸發(fā),提交表單。 Form 模塊處理的是表單提交。表單提交包含兩部分,一部分是格式化表單數(shù)據(jù),另一部分是觸發(fā) submit 事件,提交表單。 讀 Zepto 源碼系列文章已...
摘要:所以模塊依賴于模塊,在引入前必須引入模塊。原有的方法分析見(jiàn)讀源碼之樣式操作方法首先調(diào)用原有的方法,將元素顯示出來(lái),這是實(shí)現(xiàn)動(dòng)畫(huà)的基本條件。如果沒(méi)有傳遞,或者為值,則表示不需要?jiǎng)赢?huà),調(diào)用原有的方法即可。 fx 模塊提供了 animate 動(dòng)畫(huà)方法,fx_methods 利用 animate 方法,提供一些常用的動(dòng)畫(huà)方法。所以 fx_methods 模塊依賴于 fx 模塊,在引入 fx_m...
閱讀 1478·2021-10-18 13:29
閱讀 2725·2021-10-12 10:18
閱讀 3593·2021-09-22 15:06
閱讀 2607·2019-08-29 17:09
閱讀 2798·2019-08-29 16:41
閱讀 1502·2019-08-29 13:48
閱讀 3237·2019-08-26 13:49
閱讀 3333·2019-08-26 13:34