摘要:先看下圖,我們以刪除元素,插入元素復(fù)制元素,包裹元素和替換元素幾個(gè)模塊分別探究如何一一將其實(shí)現(xiàn)。遍歷當(dāng)前集合中的元素,當(dāng)該元素的父節(jié)點(diǎn)存在的時(shí)候,使用刪除該元素。接下來(lái)我們來(lái)看如何將中創(chuàng)建好的節(jié)點(diǎn)插入到目標(biāo)位置。
前言
dom也就是文檔對(duì)象模型,是針對(duì)HTML和XML的一個(gè)api,描繪了一個(gè)層次化的節(jié)點(diǎn)樹(shù)。雖然瀏覽器原生給我們提供了許多操作dom的方法,使我們可以對(duì)dom進(jìn)行查找,復(fù)制,替換和刪除等操作。但是zepto在其基礎(chǔ)上再次封裝,給以我們更加便捷的操作方式。先看下圖,我們以刪除元素,插入元素,復(fù)制元素,包裹元素和替換元素幾個(gè)模塊分別探究zepto如何一一將其實(shí)現(xiàn)。
原文鏈接
github項(xiàng)目地址
刪除元素 remove當(dāng)父節(jié)點(diǎn)存在時(shí),從其父節(jié)點(diǎn)中刪除當(dāng)前集合中的元素。
remove: function () { return this.each(function () { if (this.parentNode != null) this.parentNode.removeChild(this) }) }
遍歷當(dāng)前集合中的元素,當(dāng)該元素的父節(jié)點(diǎn)存在的時(shí)候,使用removeChild刪除該元素。
detach功能和remove一樣,都是刪除元素。
$.fn.detach = $.fn.remove
可以看到就是在$的原型上添加了一個(gè)指向remove函數(shù)的方法detach。
empty清空對(duì)象集合中每個(gè)元素的DOM內(nèi)容
empty: function () { return this.each(function () { this.innerHTML = "" }) },
遍歷當(dāng)前集合中的元素,然后將元素的innerHTML屬性設(shè)置為空。也就達(dá)到了清除DOM內(nèi)容的目的。
插入元素append, prepend, after, before插入元素的相關(guān)api比較多,我們先來(lái)重溫部分api的使用用法和比較一下他們之間的區(qū)別。
let $box = $(".box") let insertDom = "
以上是append,appendTo,prepend,prependTo,after,insertAfter,before,insertBefore八個(gè)方法的基本用法,以及用過(guò)之后的dom結(jié)構(gòu)。我們總結(jié)一下他們的區(qū)別。
首先每個(gè)方法的入?yún)⒍伎梢詾閔tml字符串,dom節(jié)點(diǎn),或者節(jié)點(diǎn)組成的數(shù)組。參考自zeptojs_api
append,appendTo,prepend,prependTo都是在元素內(nèi)部插入內(nèi)容,而after,insertAfter,before,insertBefore則是在元素外部插入內(nèi)容。
append,appendTo是在元素的末尾插入內(nèi)容,prepend,prependTo是在元素的初始位置插入,after,insertAfter是在元素的后面插入內(nèi)容,before,insertBefore則是在元素的前面插入內(nèi)容
接下來(lái)我們開(kāi)始學(xué)習(xí)和閱讀實(shí)現(xiàn)這8大方法的核心源碼部分
adjacencyOperators = ["after", "prepend", "before", "append"] adjacencyOperators.forEach(function(operator, operatorIndex) { var inside = operatorIndex % 2 $.fn[operator] = function() { // arguments can be nodes, arrays of nodes, Zepto objects and HTML strings var argType, nodes = $.map(arguments, function(arg) { var arr = [] argType = type(arg) if (argType == "array") { arg.forEach(function(el) { if (el.nodeType !== undefined) return arr.push(el) else if ($.zepto.isZ(el)) return arr = arr.concat(el.get()) arr = arr.concat(zepto.fragment(el)) }) return arr } return argType == "object" || arg == null ? arg : zepto.fragment(arg) }), parent, copyByClone = this.length > 1 if (nodes.length < 1) return this return this.each(function(_, target) { parent = inside ? target : target.parentNode // convert all methods to a "before" operation target = operatorIndex == 0 ? target.nextSibling : operatorIndex == 1 ? target.firstChild : operatorIndex == 2 ? target : null var parentInDocument = $.contains(document.documentElement, parent) nodes.forEach(function(node) { if (copyByClone) node = node.cloneNode(true) else if (!parent) return $(node).remove() parent.insertBefore(node, target) if (parentInDocument) traverseNode(node, function(el) { if (el.nodeName != null && el.nodeName.toUpperCase() === "SCRIPT" && (!el.type || el.type === "text/javascript") && !el.src) { var target = el.ownerDocument ? el.ownerDocument.defaultView : window target["eval"].call(target, el.innerHTML) } }) }) }) }
遍歷adjacencyOperators數(shù)組給$原型添加對(duì)應(yīng)的方法
adjacencyOperators = ["after", "prepend", "before", "append"] adjacencyOperators.forEach(function(operator, operatorIndex) { // xxx $.fn[operator] = function() { // xxx } // xxx })
可以看到通過(guò)循環(huán)遍歷adjacencyOperators從而給$的原型添加對(duì)應(yīng)的方法。
轉(zhuǎn)換node節(jié)點(diǎn)
var argType, nodes = $.map(arguments, function(arg) { var arr = [] argType = type(arg) if (argType == "array") { arg.forEach(function(el) { if (el.nodeType !== undefined) return arr.push(el) else if ($.zepto.isZ(el)) return arr = arr.concat(el.get()) arr = arr.concat(zepto.fragment(el)) }) return arr } return argType == "object" || arg == null ? arg : zepto.fragment(arg) })
例子
// 1 html字符串 $box.append("hello world") // 2 dom節(jié)點(diǎn) $box.append(document.createElement("span")) // 3 多個(gè)參數(shù) $box.append("1", "2") // 4 數(shù)組 $box.append(["hello world", document.createElement("span")])
因?yàn)閭魅氲膬?nèi)容可以為html字符串,dom節(jié)點(diǎn),或者節(jié)點(diǎn)組成的數(shù)組。這里對(duì)可能的情況分類(lèi)型做了處理。通過(guò)內(nèi)部的type函數(shù)判斷每個(gè)參數(shù)的數(shù)據(jù)類(lèi)型并保存在argType中。
當(dāng)參數(shù)類(lèi)型為數(shù)組(類(lèi)似上面例子中的4)的時(shí)候,再對(duì)該參數(shù)進(jìn)行遍歷,如果該參數(shù)中的元素存在nodeType屬性則將該元素推進(jìn)數(shù)組arr,
如果該參數(shù)中的元素是一個(gè)Zepto對(duì)象,則調(diào)用get方法,將arr與返回的原生元素?cái)?shù)組進(jìn)行合并。
當(dāng)參數(shù)類(lèi)型為object或者null的時(shí)候直接返回,否則就是處理字符串形式了,通過(guò)調(diào)用zepto.fragment(這個(gè)函數(shù)在后面的文章中會(huì)詳細(xì)講解,現(xiàn)在就其理解為將html字符串處理成dom節(jié)點(diǎn)數(shù)組就可以了)處理并將結(jié)果返回。
到現(xiàn)在為止,我們已經(jīng)明白了怎么將傳入的content轉(zhuǎn)化為對(duì)應(yīng)的dom節(jié)點(diǎn)。
接下來(lái)我們來(lái)看如何將nodes中創(chuàng)建好的dom節(jié)點(diǎn)插入到目標(biāo)位置。
parent, copyByClone = this.length > 1 if (nodes.length < 1) return this
先留意一下parent,以及copyByClone這兩個(gè)變量,挺重要的,具體作用下面會(huì)詳細(xì)說(shuō)明。并且如果需要插入的元素?cái)?shù)組的長(zhǎng)度小于1,那么也就沒(méi)有必要繼續(xù)往下走了,直接return this進(jìn)行鏈?zhǔn)讲僮鳌?/p>
return this.each(function(_, target) { // xxx nodes.forEach(function(node) { // xxx // 注意這行,所有的插入操作都通過(guò)insertBefore函數(shù)完成 parent.insertBefore(node, target) // xxx }) })
整個(gè)后續(xù)代碼就是兩層嵌套循環(huán),第一層遍歷當(dāng)前選中的元素集合,第二層就是需要插入的nodes節(jié)點(diǎn)集合。通過(guò)兩個(gè)循環(huán)來(lái)最終完成元素的插入操作,并且很重要的一點(diǎn)是,不管是append還是after等方法都是通過(guò)insertBefore來(lái)模擬完成的。
確定parent節(jié)點(diǎn)以及target目標(biāo)節(jié)點(diǎn)
通過(guò)上面的分析我們知道通過(guò)insertBefore(在當(dāng)前節(jié)點(diǎn)的某個(gè)子節(jié)點(diǎn)之前再插入一個(gè)子節(jié)點(diǎn))來(lái)完成節(jié)點(diǎn)的插入,很重要的幾個(gè)因素就是
parentNode.insertBefore(newNode, referenceNode)
父節(jié)點(diǎn)(parentNode)
需要插入的新節(jié)點(diǎn)(newNode)
參考節(jié)點(diǎn)referenceNode
所以確定以上1和3就顯得極其重要了。怎么確定呢?
return this.each(function(_, target) { parent = inside ? target : target.parentNode // convert all methods to a "before" operation target = operatorIndex == 0 ? target.nextSibling : operatorIndex == 1 ? target.firstChild : operatorIndex == 2 ? target : null // xxx })
inside是個(gè)啥啊!!!,讓我們回到頂部看這段
adjacencyOperators = ["after", "prepend", "before", "append"] adjacencyOperators.forEach(function (operator, operatorIndex) { var inside = operatorIndex % 2 // xxx })
所以說(shuō)當(dāng)要往$原型上添加的方法是prepend和append的時(shí)候inside為1也就是真,當(dāng)為after和before的時(shí)候?yàn)?也就是假。
因?yàn)?b>prepend和append都是往當(dāng)前選中的元素內(nèi)部添加新節(jié)點(diǎn),所以parent當(dāng)然就是target本身了,但是after和before確是要往選中的元素外部添加新節(jié)點(diǎn),自然parent就變成了當(dāng)前選中元素的父節(jié)點(diǎn)。到這里上面的三要素1,已經(jīng)明確了,還有3(target)如何確定呢?
target = operatorIndex == 0 ? target.nextSibling : operatorIndex == 1 ? target.firstChild : operatorIndex == 2 ? target : null
如果operatorIndex為0,即after方法,node節(jié)點(diǎn)應(yīng)該是插入到目標(biāo)元素target的后面,也就是target的下一個(gè)兄弟節(jié)點(diǎn)的前面
如果operatorIndex為1,即prepend方法,node應(yīng)該插入到目標(biāo)元素target的第一個(gè)子元素的前面
如果operatorIndex為2,即before方法,node節(jié)點(diǎn)應(yīng)該插入到target節(jié)點(diǎn)的前面
否則operatorIndex為4了,即append方法,node節(jié)點(diǎn)應(yīng)該插入到target最后一個(gè)子節(jié)點(diǎn)的末尾,insertBefore傳入null,正好與其功能相對(duì)應(yīng)
好啦三要素3頁(yè)已經(jīng)明確了,接下來(lái)我們把重要放在第二個(gè)循環(huán)。
將新節(jié)點(diǎn)插入到指定位置
nodes.forEach(function(node) { if (copyByClone) node = node.cloneNode(true) else if (!parent) return $(node).remove() parent.insertBefore(node, target) // 處理插入script情況 })
在將節(jié)點(diǎn)插入到指定位置的前有一個(gè)判斷,如果copyByClone為真,就將要插入的新節(jié)點(diǎn)復(fù)制一份。為什么要這么做呢?我們來(lái)看個(gè)例子。
let $list = document.querySelector(".list") let $listLi = document.querySelectorAll(".list li") let createEle = (tagName, text) => { let ele = document.createElement(tagName) ele.innerHTML = text return ele } let $span1 = createEle("span", "span1") let $span2 = createEle("span", "span2") Array.from($listLi).forEach((target) => { [$span1, $span2].forEach((node) => { // node = node.cloneNode(true) $list.insertBefore(node, target) }) })
先將cloneNode那部分給注銷(xiāo)了,我們期望往三個(gè)li的前面都插入兩個(gè)span,但是結(jié)果會(huì)怎么樣呢?只有最后一個(gè)節(jié)點(diǎn)前面可以成功地插入兩個(gè)span節(jié)點(diǎn)。這樣就不是我們先要的結(jié)果了,根據(jù)insertBefore mdn解釋?zhuān)?strong>如果newElement已經(jīng)在DOM樹(shù)中,newElement首先會(huì)從DOM樹(shù)中移除。,所以當(dāng)我們需要往多個(gè)li中插入同樣類(lèi)似的兩個(gè)節(jié)點(diǎn)的時(shí)候,才需要將新節(jié)點(diǎn)克隆一份再插入。
我們接著回到源碼。
nodes.forEach(function(node) { if (copyByClone) node = node.cloneNode(true) else if (!parent) return $(node).remove() parent.insertBefore(node, target) // 處理插入script情況 })
如果需要(當(dāng)前選中元素的個(gè)數(shù)大于1)克隆節(jié)點(diǎn)的時(shí)候,先將新節(jié)點(diǎn)克隆一份,如果沒(méi)有找到對(duì)應(yīng)的parent節(jié)點(diǎn),就講要插入的新節(jié)點(diǎn)刪除,最后通過(guò)insertBefore方法插入新節(jié)點(diǎn)。
到了這里我們似乎已經(jīng)完成了從
創(chuàng)建新節(jié)點(diǎn) => 將新節(jié)點(diǎn)插入到指定位置的操作了。任務(wù)好像已經(jīng)完成了,但是革命尚未成功,同志仍需努力啊。接下來(lái)看最后一點(diǎn)代碼,主要是處理,當(dāng)插入的節(jié)點(diǎn)是script
標(biāo)簽的時(shí)候,需要手動(dòng)去執(zhí)行其包含的js代碼。
var parentInDocument = $.contains(document.documentElement, parent) if (parentInDocument) traverseNode(node, function(el) { if (el.nodeName != null && el.nodeName.toUpperCase() === "SCRIPT" && (!el.type || el.type === "text/javascript") && !el.src) { var target = el.ownerDocument ? el.ownerDocument.defaultView : window target["eval"].call(target, el.innerHTML) } })
先提前看一下traverseNode這個(gè)函數(shù)的代碼
function traverseNode(node, fun) { fun(node) for (var i = 0, len = node.childNodes.length; i < len; i++) traverseNode(node.childNodes[i], fun) }
這個(gè)函數(shù)的主要作用就是將傳入的node節(jié)點(diǎn)作為參數(shù)去調(diào)用傳入的fun函數(shù)。并且遞歸的將node節(jié)點(diǎn)的子節(jié)點(diǎn),交給fun去處理。
接下來(lái)繼續(xù)看。
首先通過(guò)$.contains方法判斷parent是否在document文檔中,接著需要滿(mǎn)足一下幾個(gè)條件才去執(zhí)行后續(xù)操作。
存在nodeName屬性
nodeName是script標(biāo)簽
type屬性為空或者type屬性為text/javascript
src屬性為空(即不指定外部腳本)
確定window對(duì)象
var target = el.ownerDocument ? el.ownerDocument.defaultView : window
新節(jié)點(diǎn)存在ownerDocument mdn則window對(duì)象為defaultView mdn,否則使用window對(duì)象本身。
這里主要會(huì)考慮node節(jié)點(diǎn)是iframe種的元素情況,才需要做三目處理。
最后便是調(diào)用target["eval"].call(target, el.innerHTML)去執(zhí)行script中的代碼了。
到這里我們終于知道了"after", "prepend", "before", "append"實(shí)現(xiàn)全過(guò)程(偷樂(lè)一下?,不容易啊)。
appendTo, prependTo, insertBefore, insertAfter緊接著我們繼續(xù)往前走,前面說(shuō)了插入操作有很多個(gè)方法,其中
insertAfter,insertBefore,prependTo,appendTo的實(shí)現(xiàn)基于上述幾個(gè)方法。
// append => appendTo // prepend => prependTo // before => insertBefore // after => insertAfter $.fn[inside ? operator + "To" : "insert" + (operatorIndex ? "Before" : "After")] = function (html) { $(html)[operator](this) return this }
如果是append或者prepend則往$原型上添加appendTo和prependTo方法,如果是before或者after的時(shí)候,便往$的原型上添加insertBefore和insertAfter方法。因?yàn)槠鋬蓛蓪?duì)應(yīng)的方法本質(zhì)上是同樣的功能,只是在使用上有點(diǎn)相反的意思,所以簡(jiǎn)單的反向調(diào)用一下就可以了。
html獲取或設(shè)置對(duì)象集合中元素的HTML內(nèi)容。當(dāng)沒(méi)有給定content參數(shù)時(shí),返回對(duì)象集合中第一個(gè)元素的innerHtml。當(dāng)給定content參數(shù)時(shí),用其替換對(duì)象集合中每個(gè)元素的內(nèi)容。content可以是append中描述的所有類(lèi)型 zeptojs_api
例子
1. html() ? string 2. html(content) ? self 3. html(function(index, oldHtml){ ... }) ? self
源碼實(shí)現(xiàn)
html: function (html) { return 0 in arguments ? this.each(function (idx) { var originHtml = this.innerHTML $(this).empty().append(funcArg(this, html, idx, originHtml)) }) : (0 in this ? this[0].innerHTML : null) }
當(dāng)沒(méi)有傳html參數(shù)的時(shí)候,先判斷當(dāng)前選中的元素是否存在,存在則讀取第一個(gè)元素的innerHTML并返回,否則直接返回null
(0 in this ? this[0].innerHTML : null)
當(dāng)傳了html參數(shù)的時(shí)候。對(duì)當(dāng)前選中的元素集合進(jìn)行遍歷設(shè)置,先保存當(dāng)前元素的innerHTML到originHtml變量中,再將當(dāng)前元素的innerHTML置空,并將funcArg函數(shù)執(zhí)行之后返回的html插入到當(dāng)前元素中。
function funcArg(context, arg, idx, payload) { return isFunction(arg) ? arg.call(context, idx, payload) : arg }
可以看到funcArg會(huì)對(duì)傳入arg進(jìn)行類(lèi)型判斷,如果是函數(shù),就把對(duì)應(yīng)的參數(shù)傳入函數(shù)再將函數(shù)的執(zhí)行結(jié)果返回,不是函數(shù)就直接返回arg。
text獲取或者設(shè)置所有對(duì)象集合中元素的文本內(nèi)容。當(dāng)沒(méi)有給定content參數(shù)時(shí),返回當(dāng)前對(duì)象集合中第一個(gè)元素的文本內(nèi)容(包含子節(jié)點(diǎn)中的文本內(nèi)容)。當(dāng)給定content參數(shù)時(shí),使用它替換對(duì)象集合中所有元素的文本內(nèi)容。它有待點(diǎn)似 html,與它不同的是它不能用來(lái)獲取或設(shè)置 HTML。zeptojs_api
text: function (text) { return 0 in arguments ? this.each(function (idx) { var newText = funcArg(this, text, idx, this.textContent) this.textContent = newText == null ? "" : "" + newText }) : (0 in this ? this.pluck("textContent").join("") : null) }
text實(shí)現(xiàn)方法與html比較類(lèi)似有些不同的是沒(méi)有傳參數(shù)的時(shí)候,html是獲取第一個(gè)元素的innerHTMLtext則是將當(dāng)前所有元素的textContent拼接起來(lái)并返回.
復(fù)制元素 clone通過(guò)深度克隆來(lái)復(fù)制集合中的所有元素。zeptojs_api
clone: function () { return this.map(function () { return this.cloneNode(true) }) }
對(duì)當(dāng)前選中的元素集合進(jìn)行遍歷操作,底層還是用的瀏覽器cloneNode,并傳參為true表示需要進(jìn)行深度克隆(其實(shí)感覺(jué)這里是不是將true設(shè)置為可選參數(shù)比較好呢,讓使用者決定是深度克隆與否不是更合理?)
需要注意的地方是cloneNode方法不會(huì)復(fù)制添加到DOM節(jié)點(diǎn)中的Javascript屬性,例如事件處理程序等,這個(gè)方法只復(fù)制特性,子節(jié)點(diǎn),其他一切都不會(huì)復(fù)制,IE在此存在一個(gè)bug,即他會(huì)賦值事件處理程序,所以我們建議在賦值之間最好先移除事件處理程序(摘自《JavaScript高級(jí)程序設(shè)計(jì)第三版》10.1.1 Node類(lèi)型小字部分)
替換元素 replaceWidth用給定的內(nèi)容替換所有匹配的元素。(包含元素本身) zeptojs_api
replaceWith: function(newContent) { return this.before(newContent).remove() }
源碼實(shí)現(xiàn)其實(shí)很簡(jiǎn)單分兩步,第一步調(diào)用前面我們講的before方法將制定newContent插入到元素的前面,第二部步將當(dāng)前選中的元素刪除。自然也就達(dá)到了替換的目的。
包裹元素 wrapAll在所有匹配元素外面包一個(gè)多帶帶的結(jié)構(gòu)。結(jié)構(gòu)可以是單個(gè)元素或 幾個(gè)嵌套的元素zeptojs_api/#wrapAll
wrapAll: function (structure) { // 如果選中的元素存在 if (this[0]) { // 則將制定structure結(jié)構(gòu)通過(guò)before方法,插入到選中的第一個(gè)元素的前面 $(this[0]).before(structure = $(structure)) var children // drill down to the inmost element // 獲取structure的最深層次的第一個(gè)子元素 while ((children = structure.children()).length) structure = children.first() // 將當(dāng)前的元素集合通過(guò)append方法添加到structure末尾 $(structure).append(this) } // 反則直接返回this進(jìn)行后續(xù)的鏈?zhǔn)讲僮? return this }
源碼實(shí)現(xiàn)直接看注釋就可以了,這里需要注意一下children函數(shù)是獲取對(duì)象集合中所有的直接子節(jié)點(diǎn)。而first函數(shù)則是獲取當(dāng)前集合的第一個(gè)元素。
另外我們看一下下面兩個(gè)例子。
$(".box").wrapAll(".wrap")
執(zhí)行上述代碼之后dom結(jié)構(gòu)會(huì)變成
- 1
- 2
- 1
- 2
可以看到原來(lái)ul結(jié)構(gòu)還是存在,仿佛是復(fù)制了一份ul及其子節(jié)點(diǎn)到wrap中被包裹起來(lái)。
接下來(lái)再看一個(gè)例子,唯一的區(qū)別就在wrap結(jié)構(gòu)中嵌套了基層。
但是最后執(zhí)行$(".box").wrapAll(".wrap")得到的dom結(jié)果是。
- 1
- 2
嘿嘿可以看到,ul原來(lái)的結(jié)構(gòu)不見(jiàn)了,被移動(dòng)到了第一個(gè)wrap的第一個(gè)子節(jié)點(diǎn)here中。具體原因是什么呢?大家可以重新回去看一下append的核心實(shí)現(xiàn)。
wrap在每個(gè)匹配的元素外層包上一個(gè)html元素。structure參數(shù)可以是一個(gè)多帶帶的元素或者一些嵌套的元素。也可以是一個(gè)html字符串片段或者dom節(jié)點(diǎn)。還可以是一個(gè)生成用來(lái)包元素的回調(diào)函數(shù),這個(gè)函數(shù)返回前兩種類(lèi)型的包裹片段。zeptojs_api/#wrapAll
wrap: function (structure) { var func = isFunction(structure) // 當(dāng)前選中的元素不為空,并且structure不是一個(gè)函數(shù) if (this[0] && !func) // 就將structure轉(zhuǎn)化后的第一個(gè)元素賦值給dom元素 var dom = $(structure).get(0), // 如果dom元素的parentNode存在或者當(dāng)前選中的元素個(gè)數(shù)大于1那么clone為true clone = dom.parentNode || this.length > 1 // 對(duì)當(dāng)前選中元素進(jìn)行遍歷并且調(diào)用wrapAll方法 return this.each(function (index) { $(this).wrapAll( // 如果structure為函數(shù),則將當(dāng)前的元素和對(duì)應(yīng)的索引傳入函數(shù) func ? structure.call(this, index) : // 如果clone為true,則使用拷貝的副本 clone ? dom.cloneNode(true) : dom ) }) }wrapInner
將每個(gè)元素中的內(nèi)容包裹在一個(gè)多帶帶的結(jié)構(gòu)中 zeptojs_api/#wrapInner
wrapInner: function (structure) { // 判斷structure是否為函數(shù) var func = isFunction(structure) // 對(duì)當(dāng)前元素集合進(jìn)行遍歷處理 return this.each(function (index) { // contents => 獲取當(dāng)前元素的所有子節(jié)點(diǎn)(包括元素節(jié)點(diǎn)和文本節(jié)點(diǎn)) var self = $(this), contents = self.contents(), // structure為函數(shù)則將其執(zhí)行結(jié)果賦值為dom,否則直接將其賦值 dom = func ? structure.call(this, index) : structure // 當(dāng)前元素的子節(jié)點(diǎn)不為空,則調(diào)用wrapAll,否則直接將dom插入self當(dāng)前元素即可 contents.length ? contents.wrapAll(dom) : self.append(dom) }) }
需要注意的是這個(gè)函數(shù)和前面的wrapAll和wrap有點(diǎn)不一樣,這里強(qiáng)調(diào)的是將當(dāng)前元素中的內(nèi)容(包括元素節(jié)點(diǎn)和文本節(jié)點(diǎn))進(jìn)行包裹。
unwrap移除集合中每個(gè)元素的直接父節(jié)點(diǎn),并把他們的子元素保留在原來(lái)的位置
unwrap: function () { // 通過(guò)parent()獲取當(dāng)前元素集合的所有直接父節(jié)點(diǎn) // 將獲取到的父節(jié)點(diǎn)集合進(jìn)行遍歷 this.parent().each(function () { // 將該父節(jié)點(diǎn)替換為該父節(jié)點(diǎn)的所有子節(jié)點(diǎn) $(this).replaceWith($(this).children()) }) return this },結(jié)尾
參考呼呼呼,終于寫(xiě)完了,快累死了。歡迎大家指正文中的問(wèn)題。
讀Zepto源碼之操作DOM
Zepto源碼分析-zepto模塊
ownerDocument
insertBefore
innerHTML
《JavaScript高級(jí)程序設(shè)計(jì)第三版》
文章記錄
form模塊
zepto源碼分析之form模塊(2017-10-01)
zepto模塊
這些Zepto中實(shí)用的方法集(2017-08-26)
Zepto核心模塊之工具方法拾遺 (2017-08-30)
看zepto如何實(shí)現(xiàn)增刪改查DOM (2017-10-2)
event模塊
mouseenter與mouseover為何這般糾纏不清?(2017-06-05)
向zepto.js學(xué)習(xí)如何手動(dòng)觸發(fā)DOM事件(2017-06-07)
誰(shuí)說(shuō)你只是"會(huì)用"jQuery?(2017-06-08)
ajax模塊
原來(lái)你是這樣的jsonp(原理與具體實(shí)現(xiàn)細(xì)節(jié))(2017-06-11)
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/116661.html
摘要:先看下圖,我們以刪除元素,插入元素復(fù)制元素,包裹元素和替換元素幾個(gè)模塊分別探究如何一一將其實(shí)現(xiàn)。遍歷當(dāng)前集合中的元素,當(dāng)該元素的父節(jié)點(diǎn)存在的時(shí)候,使用刪除該元素。接下來(lái)我們來(lái)看如何將中創(chuàng)建好的節(jié)點(diǎn)插入到目標(biāo)位置。 前言 dom也就是文檔對(duì)象模型,是針對(duì)HTML和XML的一個(gè)api,描繪了一個(gè)層次化的節(jié)點(diǎn)樹(shù)。雖然瀏覽器原生給我們提供了許多操作dom的方法,使我們可以對(duì)dom進(jìn)行查找,復(fù)制...
摘要:先看下圖,我們以刪除元素,插入元素復(fù)制元素,包裹元素和替換元素幾個(gè)模塊分別探究如何一一將其實(shí)現(xiàn)。遍歷當(dāng)前集合中的元素,當(dāng)該元素的父節(jié)點(diǎn)存在的時(shí)候,使用刪除該元素。接下來(lái)我們來(lái)看如何將中創(chuàng)建好的節(jié)點(diǎn)插入到目標(biāo)位置。 前言 dom也就是文檔對(duì)象模型,是針對(duì)HTML和XML的一個(gè)api,描繪了一個(gè)層次化的節(jié)點(diǎn)樹(shù)。雖然瀏覽器原生給我們提供了許多操作dom的方法,使我們可以對(duì)dom進(jìn)行查找,復(fù)制...
摘要:有一個(gè)模塊,專(zhuān)門(mén)用來(lái)做數(shù)據(jù)緩存,允許我們存放任何與相關(guān)的數(shù)據(jù)。在匹配元素上存儲(chǔ)任意相關(guān)數(shù)據(jù)或返回匹配的元素集合中的第一個(gè)元素的給定名稱(chēng)的數(shù)據(jù)存儲(chǔ)的值。確定元素是否有與之相關(guān)的數(shù)據(jù)。 前言 以前我們使用Zepto進(jìn)行開(kāi)發(fā)的時(shí)候,會(huì)把一些自定義的數(shù)據(jù)存到dom節(jié)點(diǎn)上,好處是非常直觀(guān)和便捷,但是也帶來(lái)了例如直接將數(shù)據(jù)暴露出來(lái)會(huì)出現(xiàn)安全問(wèn)題,數(shù)據(jù)以html自定義屬性標(biāo)簽存在,對(duì)于瀏覽器本身來(lái)說(shuō)...
摘要:獲得當(dāng)前元素相對(duì)于的位置。返回一個(gè)對(duì)象含有和當(dāng)給定一個(gè)含有和屬性對(duì)象時(shí),使用這些值來(lái)對(duì)集合中每一個(gè)元素進(jìn)行相對(duì)于的定位。獲取對(duì)象集合中第一個(gè)元素相對(duì)于其的位置。結(jié)尾以上就是中與偏移相關(guān)的幾個(gè)的解析,歡迎指出其中的問(wèn)題和有錯(cuò)誤的地方。 前言 這篇文章主要想說(shuō)一下Zepto中與偏移相關(guān)的一些事,很久很久以前,我們經(jīng)常會(huì)使用offset、position、scrollTop、scrollLe...
閱讀 2814·2019-08-30 15:55
閱讀 2861·2019-08-30 15:53
閱讀 2298·2019-08-26 13:47
閱讀 2561·2019-08-26 13:43
閱讀 3160·2019-08-26 13:33
閱讀 2808·2019-08-26 11:53
閱讀 1801·2019-08-23 18:35
閱讀 804·2019-08-23 17:16