摘要:方法是將集合中不符合條件的元素查找出來(lái)。判斷集合中的第一個(gè)元素是否匹配指定的選擇器。這個(gè)在讀源碼之集合操作有講過(guò),如果集合個(gè)數(shù)大于零,則表示滿足條件。返回集合中所有元素指定的屬性值。獲取集合中每個(gè)元素的前一個(gè)兄弟節(jié)點(diǎn)。
這篇依然是跟 dom 相關(guān)的方法,側(cè)重點(diǎn)是跟集合元素查找相關(guān)的方法。
讀Zepto源碼系列文章已經(jīng)放到了github上,歡迎star: reading-zepto
源碼版本本文閱讀的源碼為 zepto1.2.0
內(nèi)部方法之前有一章《讀Zepto源碼之內(nèi)部方法》是專門解讀 zepto 中沒(méi)有提供給外部使用的內(nèi)部方法的,但是有幾個(gè)涉及到 dom 的方法沒(méi)有解讀,這里先將本章用到的方法解讀一下。
matcheszepto.matches = function(element, selector) { if (!selector || !element || element.nodeType !== 1) return false var matchesSelector = element.matches || element.webkitMatchesSelector || element.mozMatchesSelector || element.oMatchesSelector || element.matchesSelector if (matchesSelector) return matchesSelector.call(element, selector) // fall back to performing a selector: var match, parent = element.parentNode, temp = !parent if (temp)(parent = tempParent).appendChild(element) match = ~zepto.qsa(parent, selector).indexOf(element) temp && tempParent.removeChild(element) return match }
matches 方法用于檢測(cè)元素( element )是否匹配特定的選擇器( selector )。
瀏覽器也有原生的 matches 方法,但是要到IE9之后才支持。具體見(jiàn)文檔:Element.matches()
if (!selector || !element || element.nodeType !== 1) return false
這段是確保 selector 和 element 兩個(gè)參數(shù)都有傳遞,并且 element 參數(shù)的 nodeType 為 ELEMENT_NODE ,如何條件不符合,返回 false
var matchesSelector = element.matches || element.webkitMatchesSelector || element.mozMatchesSelector || element.oMatchesSelector || element.matchesSelector if (matchesSelector) return matchesSelector.call(element, selector)
這段是檢測(cè)瀏覽器是否原生支持 matches 方法,或者支持帶私有前綴的 matches 方法,如果支持,調(diào)用原生的 matches ,并將結(jié)果返回。
var match, parent = element.parentNode, temp = !parent if (temp)(parent = tempParent).appendChild(element) match = ~zepto.qsa(parent, selector).indexOf(element) temp && tempParent.removeChild(element) return match
如果原生的方法不支持,則回退到用選擇器的方法來(lái)檢測(cè)。
這里定義了三個(gè)變量,其中 parent 用來(lái)存放 element 的父節(jié)點(diǎn), temp 用來(lái)判斷 element 是否有父元素。值為 temp = !parent ,如果 element 存在父元素,則 temp 的值為 false 。
首先判斷是否存在父元素,如果父元素不存在,則 parent = tempParent ,tempParent 已經(jīng)由一個(gè)全局變量來(lái)定義,為 tempParent = document.createElement("div") ,其實(shí)就是一個(gè) div 空節(jié)點(diǎn)。然后將 element 插入到空節(jié)點(diǎn)中。
然后,查找 parent 中所有符合選擇器 selector 的元素集合,再找出當(dāng)前元素 element 在集合中的索引。
zepto.qsa(parent, selector).indexOf(element)
再對(duì)索引進(jìn)行取反操作,這樣索引值為 0 的值就變成了 -1,是 -1 的返回的是 0,這樣就確保了跟 matches 的表現(xiàn)一致。
其實(shí)我有點(diǎn)不太懂的是,為什么不跟原生一樣,返回 boolean 類型的值呢?明明通過(guò) zepto.qsa(parent, selector).indexOf(element) > -1 就可以做到了,接口表現(xiàn)一致不是更好嗎?
最后還有一步清理操作:
temp && tempParent.removeChild(element)
將空接點(diǎn)的子元素清理點(diǎn),避免污染。
childrenfunction children(element) { return "children" in element ? slice.call(element.children) : $.map(element.childNodes, function(node) { if (node.nodeType == 1) return node }) }
children 方法返回的是 element 的子元素集合。
瀏覽器也有原生支持元素 children 屬性,也要到IE9以上才支持,見(jiàn)文檔ParentNode.children
如果檢測(cè)到瀏覽器不支持,則降級(jí)用 $.map 方法,獲取 element 的 childNodes 中 nodeType 為 ELEMENT_NODE 的節(jié)點(diǎn)。因?yàn)?children 返回的只是元素節(jié)點(diǎn),但是 childNodes 返回的除元素節(jié)點(diǎn)外,還包含文本節(jié)點(diǎn)、屬性等。
這里用到的 $.map 跟數(shù)組的原生方法 map 表現(xiàn)有區(qū)別,關(guān)于 $.map 的具體實(shí)現(xiàn),已經(jīng)在《讀zepto源碼之工具函數(shù)》解讀過(guò)了。
filteredfunction filtered(nodes, selector) { return selector == null ? $(nodes) : $(nodes).filter(selector) }
將匹配指定選擇器的元素從集合中過(guò)濾出來(lái)。
如果沒(méi)有指定 selector ,則將集合包裹成 zepto 對(duì)象全部返回,否則調(diào)用 filter 方法,過(guò)濾出符合條件的元素返回。filter 方法下面馬上講到。
元素方法這里的方法都是 $.fn 中提供的方法。
.filter()filter: function(selector) { if (isFunction(selector)) return this.not(this.not(selector)) return $(filter.call(this, function(element) { return zepto.matches(element, selector) })) }
filter 是查找符合條件的元素集合。
參數(shù) selector 可以為 Function 或者選擇器,當(dāng)為 Function 時(shí),調(diào)用的其實(shí)調(diào)用了兩次 not 方法,負(fù)負(fù)得正。關(guān)于 not 方法,下面馬上會(huì)看到。
當(dāng)為一般的選擇器時(shí),調(diào)用的是filter 方法,filter 的回調(diào)函數(shù)調(diào)用了 matches ,將符合 selector 的元素返回,并包裝成 zepto 對(duì)象返回。
.not()not: function(selector) { var nodes = [] if (isFunction(selector) && selector.call !== undefined) this.each(function(idx) { if (!selector.call(this, idx)) nodes.push(this) }) else { var excludes = typeof selector == "string" ? this.filter(selector) : (likeArray(selector) && isFunction(selector.item)) ? slice.call(selector) : $(selector) this.forEach(function(el) { if (excludes.indexOf(el) < 0) nodes.push(el) }) } return $(nodes) }
not 方法是將集合中不符合條件的元素查找出來(lái)。
not 方法的方法有三種調(diào)用方式:
not(selector) ? collection not(collection) ? collection not(function(index){ ... }) ? collection
當(dāng) selector 為 Function ,并且有 call 方法時(shí)(isFunction(selector) && selector.call !== undefined),相關(guān)的代碼如下:
this.each(function(idx) { if (!selector.call(this, idx)) nodes.push(this) })
調(diào)用 each 方法,并且在 selector 函數(shù)中,可以訪問(wèn)到當(dāng)前的元素和元素的索引。如果 selector 函數(shù)的值取反后為 true,則將相應(yīng)的元素放入 nodes 數(shù)組中。
當(dāng) selector 不為 Function 時(shí), 定義了一個(gè)變量 excludes ,這個(gè)變量來(lái)用接收需要排除的元素集合。接下來(lái)又是一串三元表達(dá)式(zepto的特色啊)
typeof selector == "string" ? this.filter(selector)
當(dāng) selector 為 string 時(shí),調(diào)用 filter ,找出所有需要排除的元素
(likeArray(selector) && isFunction(selector.item)) ? slice.call(selector) : $(selector)
這段我剛開(kāi)始看時(shí),有點(diǎn)困惑,主要是不明白 isFunction(selector.item) 這個(gè)判斷條件,后來(lái)查了MDN文檔HTMLCollection.item,才明白 item 是 HTMLCollection 的一個(gè)方法,這個(gè)三元表達(dá)式的意思是,如果是 HTMLCollection ,則調(diào)用 slice.call 得到一個(gè)純數(shù)組,否則返回 zepto 對(duì)象。
this.forEach(function(el) { if (excludes.indexOf(el) < 0) nodes.push(el) })
遍歷集合,如果元素不在需要排除的元素集合中,將該元素 push 進(jìn) nodes 中。
not 方法最終返回的也是 zepto 對(duì)象。
.is()is: function(selector) { return this.length > 0 && zepto.matches(this[0], selector) }
判斷集合中的第一個(gè)元素是否匹配指定的選擇器。
代碼也比較簡(jiǎn)單了,選判斷集合不為空,再調(diào)用 matches 看第一個(gè)元素是否匹配。
.find()find: function(selector) { var result, $this = this if (!selector) result = $() else if (typeof selector == "object") result = $(selector).filter(function() { var node = this return emptyArray.some.call($this, function(parent) { return $.contains(parent, node) }) }) else if (this.length == 1) result = $(zepto.qsa(this[0], selector)) else result = this.map(function() { return zepto.qsa(this, selector) }) return result }
find 是查找集合中符合選擇器的所有后代元素,如果給定的是 zepto 對(duì)象或者 dom 元素,則只有他們?cè)诋?dāng)前的集合中時(shí),才返回。
fid 有三種調(diào)用方式,如下:
find(selector) ? collection find(collection) ? collection find(element) ? collection
if (!selector) result = $()
如果不傳參時(shí),返回的是空的 zepto 對(duì)象。
else if (typeof selector == "object") result = $(selector).filter(function() { var node = this return emptyArray.some.call($this, function(parent) { return $.contains(parent, node) }) })
如果傳參為 object 時(shí),也就是 zepto 對(duì)象collection 和dom 節(jié)點(diǎn) element 時(shí),先將 selector 包裹成 zepto 對(duì)象,然后對(duì)這個(gè)對(duì)象過(guò)濾,返回當(dāng)前集合子節(jié)點(diǎn)中所包含的元素($.contains(parent, node))。
else if (this.length == 1) result = $(zepto.qsa(this[0], selector))
如果當(dāng)前的集合只有一個(gè)元素時(shí),直接調(diào)用 zepto.qsa 方法,取出集合的第一個(gè)元素 this[0] 作為 qsa 的第一個(gè)參數(shù)。關(guān)于 qsa 方法,已經(jīng)在《讀Zepto源碼之神奇的$》分析過(guò)了。其實(shí)就是獲取第一個(gè)元素的所有后代元素。
else result = this.map(function() { return zepto.qsa(this, selector) })
否則,調(diào)用 map 方法,對(duì)集合中每個(gè)元素都調(diào)用 qsa 方法,獲取所有元素的后代元素。這個(gè)條件其實(shí)可以與上一個(gè)條件合并的,分開(kāi)應(yīng)該是為了性能的考量。
.has()has: function(selector) { return this.filter(function() { return isObject(selector) ? $.contains(this, selector) : $(this).find(selector).size() }) },
判斷集合中是否有包含指定條件的子元素,將符合條件的元素返回。
有兩種調(diào)用方式
has(selector) ? collection has(node) ? collection
參數(shù)可以為選擇器或者節(jié)點(diǎn)。
has 其實(shí)調(diào)用的是 filter 方法,這個(gè)方法上面已經(jīng)解讀過(guò)了。filter 的回調(diào)函數(shù)中根據(jù)參數(shù)的不同情況,調(diào)用了不同的方法。
isObject(selector) 用來(lái)判斷 selector 是否為 node 節(jié)點(diǎn),如果為 node 節(jié)點(diǎn),則調(diào)用 $.contains 方法,該方法已經(jīng)在《讀Zepto源碼之工具函數(shù)》說(shuō)過(guò)了。
如果為選擇器,則調(diào)用 find 方法,然后再調(diào)用 size 方法,size 方法返回的是集合中元素的個(gè)數(shù)。這個(gè)在《讀Zepto源碼之集合操作》有講過(guò),如果集合個(gè)數(shù)大于零,則表示滿足條件。
.eq()eq: function(idx) { return idx === -1 ? this.slice(idx) : this.slice(idx, +idx + 1) },
獲取集合中指定的元素。
這里調(diào)用了 slice 方法,這個(gè)方法在上一篇《讀Zepto源碼之集合操作》已經(jīng)說(shuō)過(guò)了。如果 idx 為 -1 時(shí),直接調(diào)用 this.slice(idx) ,即取出最后一個(gè)元素,否則取 idx 至 idx + 1 之間的元素,也就是每次只取一個(gè)元素。+idx+1 前面的 + 號(hào)其實(shí)是類型轉(zhuǎn)換,確保 idx 在做加法的時(shí)候?yàn)?Number 類型。
.first()first: function() { var el = this[0] return el && !isObject(el) ? el : $(el) },
first 是取集合中第一個(gè)元素,這個(gè)方法很簡(jiǎn)單,用索引 0 就可以取出來(lái)了,也就是 this[0] 。
el && !isObject(el) 用來(lái)判斷是否為 zepto 對(duì)象,如果不是,用 $(el) 包裹,確保返回的是 zepto 對(duì)象。
.last()last: function() { var el = this[this.length - 1] return el && !isObject(el) ? el : $(el) },
last 是取集合中最后一個(gè)元素,這個(gè)的原理跟 first 一樣,只不過(guò)變成了取索引值為 this.length - 1 ,也就是最后的元素。
.closest()closest: function(selector, context) { var nodes = [], collection = typeof selector == "object" && $(selector) this.each(function(_, node) { while (node && !(collection ? collection.indexOf(node) >= 0 : zepto.matches(node, selector))) node = node !== context && !isDocument(node) && node.parentNode if (node && nodes.indexOf(node) < 0) nodes.push(node) }) return $(nodes) },
從元素本身向上查找,返回最先符合條件的元素。
這個(gè)方法也有三種調(diào)用方式
closest(selector, [context]) ? collection closest(collection) ? collection closest(element) ? collection
如果指定了 zepto 集合或者 element ,則只返回匹配給定集合或 element 的元素。
collection = typeof selector == "object" && $(selector)
這段是判斷 selector 是否為 collection 或 element ,如果是,則統(tǒng)一轉(zhuǎn)化為 zepto 集合。
然后對(duì)集合遍歷,在 each 遍歷里針對(duì)集合中每個(gè) node 節(jié)點(diǎn),都用 while 語(yǔ)句,向上查找符合條件的元素。
node && !(collection ? collection.indexOf(node) >= 0 : zepto.matches(node, selector))
這段是 while 語(yǔ)句的終止條件。 node 節(jié)點(diǎn)必須存在,如果 selector 為 zepto 集合或者 element ,也即 collection 存在, 則要找到存在于 collection 中的節(jié)點(diǎn)(collection.indexOf(node) >= 0), 否則,節(jié)點(diǎn)要匹配指定的選擇器(zepto.matches(node, selector))
在 while 循環(huán)中,是向上逐級(jí)查找節(jié)點(diǎn)的過(guò)程:
node = node !== context && !isDocument(node) && node.parentNode
當(dāng)前 node 不為指定的上下文 context 并且不為 document 節(jié)點(diǎn)時(shí),向上查找(node.parentNode)
if (node && nodes.indexOf(node) < 0) nodes.push(node)
while 循環(huán)完畢后,如果 node 節(jié)點(diǎn)存在,并且 nodes 中還不存在 node ,則將 node push 進(jìn) nodes 中。
最后返回 zepto 集合。
.pluck()pluck: function(property) { return $.map(this, function(el) { return el[property] }) },
返回集合中所有元素指定的屬性值。
這個(gè)方法很簡(jiǎn)單,就是對(duì)當(dāng)前集合遍歷,然后取元素指定的 property 值。
.parents()parents: function(selector) { var ancestors = [], nodes = this while (nodes.length > 0) nodes = $.map(nodes, function(node) { if ((node = node.parentNode) && !isDocument(node) && ancestors.indexOf(node) < 0) { ancestors.push(node) return node } }) return filtered(ancestors, selector) },
返回集合中所有元素的所有祖先元素。
nodes 的初始值為當(dāng)前集合,while 循環(huán)的條件為集合不為空。
使用 map 遍歷 nodes ,將 node 重新賦值為自身的父級(jí)元素,如果父級(jí)元素存在,并且不是 document 元素,而且還不存在于 ancestors 中時(shí),將 node 存入保存祖先元素的 ancestors 中,并且 map 回調(diào)的返回值是 node ,組成新的集合賦值給 nodes ,直到所有的祖先元素遍歷完畢,就可以退出 while 循環(huán)。
最后,調(diào)用上面說(shuō)到的 filtered 方法,找到符合 selector 的祖先元素。
.parent()parent: function(selector) { return filtered(uniq(this.pluck("parentNode")), selector) },
返回集合中所有元素的父級(jí)元素。
parents 返回的是所有祖先元素,而 parent 返回只是父級(jí)元素。
首先調(diào)用的是 this.pluck("parentNode") ,獲取所有元素的祖先元素,然后調(diào)用 uniq 對(duì)集合去重,最后調(diào)用 filtered ,返回匹配 selector 的元素集合。
.children()children: function(selector) { return filtered(this.map(function() { return children(this) }), selector) },
返回集合中所有元素的子元素。
首先對(duì)當(dāng)前集合遍歷,調(diào)用內(nèi)部方法 children 獲取當(dāng)前元素的子元素組成新的數(shù)組,再調(diào)用 filtered 方法返回匹配 selector 的元素集合。
.contents()contents: function() { return this.map(function() { return this.contentDocument || slice.call(this.childNodes) }) },
這個(gè)方法類似于 children ,不過(guò) children 對(duì) childNodes 進(jìn)行了過(guò)濾,只返回元素節(jié)點(diǎn)。contents 還返回文本節(jié)點(diǎn)和注釋節(jié)點(diǎn)。也返回 iframe 的 contentDocument
.siblings()siblings: function(selector) { return filtered(this.map(function(i, el) { return filter.call(children(el.parentNode), function(child) { return child !== el }) }), selector) },
獲取所有集合中所有元素的兄弟節(jié)點(diǎn)。
獲取兄弟節(jié)點(diǎn)的思路也很簡(jiǎn)單,對(duì)當(dāng)前集合遍歷,找到當(dāng)前元素的父元素el.parentNode,調(diào)用 children 方法,找出父元素的子元素,將子元素中與當(dāng)前元素不相等的元素過(guò)濾出來(lái)即是其兄弟元素了。
最后調(diào)用 filtered 來(lái)過(guò)濾出匹配 selector 的兄弟元素。
.prev()prev: function(selector) { return $(this.pluck("previousElementSibling")).filter(selector || "*") },
獲取集合中每個(gè)元素的前一個(gè)兄弟節(jié)點(diǎn)。
這個(gè)方法也很簡(jiǎn)單,調(diào)用 pluck 方法,獲取元素的 previousElementSibling 屬性,即為元素的前一個(gè)兄弟節(jié)點(diǎn)。再調(diào)用 filter 返回匹配 selector 的元素,最后包裹成 zepto 對(duì)象返回
.next()next: function(selector) { return $(this.pluck("nextElementSibling")).filter(selector || "*") },
next 方法跟 prev 方法類似,只不過(guò)取的是 nextElementSibling 屬性,獲取的是每個(gè)元素的下一個(gè)兄弟節(jié)點(diǎn)。
.index()index: function(element) { return element ? this.indexOf($(element)[0]) : this.parent().children().indexOf(this[0]) },
返回指定元素在當(dāng)前集合中的位置(this.indexOf($(element)[0])),如果沒(méi)有給出 element ,則返回當(dāng)前鮮紅在兄弟元素中的位置。this.parent().children() 查找的是兄弟元素。
系列文章讀Zepto源碼之代碼結(jié)構(gòu)
讀 Zepto 源碼之內(nèi)部方法
讀Zepto源碼之工具函數(shù)
讀Zepto源碼之神奇的$
讀Zepto源碼之集合操作
參考Element.matches()
ParentNode.children
Node.childNodes
HTMLCollection.item
License最后,所有文章都會(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://m.specialneedsforspecialkids.com/yun/83237.html
摘要:的模塊用來(lái)獲取節(jié)點(diǎn)中的屬性的數(shù)據(jù),和儲(chǔ)存跟相關(guān)的數(shù)據(jù)。獲取節(jié)點(diǎn)指定的緩存值。如果存在,則刪除指定的數(shù)據(jù),否則將緩存的數(shù)據(jù)全部刪除。為所有下級(jí)節(jié)點(diǎn),如果為方法,則節(jié)點(diǎn)自身也是要被移除的,所以需要將自身也加入到節(jié)點(diǎn)中。 Zepto 的 Data 模塊用來(lái)獲取 DOM 節(jié)點(diǎn)中的 data-* 屬性的數(shù)據(jù),和儲(chǔ)存跟 DOM 相關(guān)的數(shù)據(jù)。 讀 Zepto 源碼系列文章已經(jīng)放到了github上,歡...
摘要:如果偽類的參數(shù)不可以用轉(zhuǎn)換,則參數(shù)為字符串,用正則將字符串前后的或去掉,再賦值給最后執(zhí)行回調(diào),將解釋出來(lái)的參數(shù)傳入回調(diào)函數(shù)中,將執(zhí)行結(jié)果返回。重寫的方法,改過(guò)的調(diào)用的是方法,在回調(diào)函數(shù)中處理大部分邏輯。 Selector 模塊是對(duì) Zepto 選擇器的擴(kuò)展,使得 Zepto 選擇器也可以支持部分 CSS3 選擇器和 eq 等 Zepto 定義的選擇器。 在閱讀本篇文章之前,最好先閱讀《...
摘要:讀源碼系列文章已經(jīng)放到了上,歡迎源碼版本本文閱讀的源碼為改寫原有的方法模塊改寫了以上這些方法,這些方法在調(diào)用的時(shí)候,會(huì)為返回的結(jié)果添加的屬性,用來(lái)保存原來(lái)的集合。方法的分析可以看讀源碼之模塊。 Stack 模塊為 Zepto 添加了 addSelf 和 end 方法。 讀 Zepto 源碼系列文章已經(jīng)放到了github上,歡迎star: reading-zepto 源碼版本 本文閱讀的...
摘要:模塊處理的是表單提交。表單提交包含兩部分,一部分是格式化表單數(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 源碼系列文章已...
摘要:模塊是為解決移動(dòng)版加載圖片過(guò)大過(guò)多時(shí)崩潰的問(wèn)題。因?yàn)闆](méi)有處理過(guò)這樣的場(chǎng)景,所以這部分的代碼解釋不會(huì)太多,為了說(shuō)明這個(gè)問(wèn)題,我翻譯了這篇文章作為附文怎樣處理移動(dòng)端對(duì)圖片資源的限制,更詳細(xì)地解釋了這個(gè)模塊的應(yīng)用場(chǎng)景。 assets 模塊是為解決 Safari 移動(dòng)版加載圖片過(guò)大過(guò)多時(shí)崩潰的問(wèn)題。因?yàn)闆](méi)有處理過(guò)這樣的場(chǎng)景,所以這部分的代碼解釋不會(huì)太多,為了說(shuō)明這個(gè)問(wèn)題,我翻譯了《How to...
閱讀 1433·2021-09-22 15:52
閱讀 1473·2019-08-30 15:44
閱讀 903·2019-08-30 14:24
閱讀 2714·2019-08-30 13:06
閱讀 2709·2019-08-26 13:45
閱讀 2790·2019-08-26 13:43
閱讀 1027·2019-08-26 12:01
閱讀 1450·2019-08-26 11:56