摘要:還有一點(diǎn)需要注意的是方法設(shè)置或者獲取都是在操作元素的屬性,那它和,的區(qū)別在哪呢可以查看設(shè)置設(shè)置與的設(shè)置部分比較類似,既支持直接傳入普通的字符串也支持傳入回調(diào)函數(shù)。
前言
使用Zepto的時(shí)候,我們經(jīng)常會(huì)要去操作一些DOM的屬性,或元素本身的固有屬性或自定義屬性等。比如常見的有attr(),removeAttr(),prop(),removeProp(),data()等。接下來我們挨個(gè)整明白他們是如何實(shí)現(xiàn)的...點(diǎn)擊zepto模塊源碼注釋查看這篇文章對(duì)應(yīng)的解析。
原文鏈接
源碼倉庫
attr()讀取或設(shè)置dom的屬性。
如果沒有給定value參數(shù),則讀取對(duì)象集合中第一個(gè)元素的屬性值。
當(dāng)給定了value參數(shù)。則設(shè)置對(duì)象集合中所有元素的該屬性的值。當(dāng)value參數(shù)為null,那么這個(gè)屬性將被移除(類似removeAttr),多個(gè)屬性可以通過對(duì)象鍵值對(duì)的方式進(jìn)行設(shè)置。zeptojs_api/#attr
示例
// 獲取name屬性 attr(name) // 設(shè)置name屬性 attr(name, value) // 設(shè)置name屬性,不同的是使用回調(diào)函數(shù)的形式 attr(name, function(index, oldValue){ ... }) // 設(shè)置多個(gè)屬性值 attr({ name: value, name2: value2, ... })
已經(jīng)知道了如何使用attr方法,在開始分析attr實(shí)現(xiàn)源碼之前,我們先了解一下這幾個(gè)函數(shù)。
setAttribute
function setAttribute(node, name, value) { value == null ? node.removeAttribute(name) : node.setAttribute(name, value) }
它的主要作用就是設(shè)置或者刪除node節(jié)點(diǎn)的屬性。當(dāng)value為null或者undefined的時(shí)候,調(diào)用removeAttribute方法移除name屬性,否則調(diào)用setAttribute方法設(shè)置name屬性。
funcArg
function funcArg(context, arg, idx, payload) { return isFunction(arg) ? arg.call(context, idx, payload) : arg }
funcArg函數(shù)在多個(gè)地方都有使用到,主要為類似attr,prop,val等方法中第二個(gè)參數(shù)可以是函數(shù)或其他類型提供可能和便捷。
如果傳入的arg參數(shù)是函數(shù)類型,那么用context作為arg函數(shù)的執(zhí)行上下文,以及將idx和payload作為參數(shù)去執(zhí)行。否則直接返回arg參數(shù)。
好啦接下來開始看attr的源碼實(shí)現(xiàn)了
attr: function (name, value) { var result return (typeof name == "string" && !(1 in arguments)) ? // 獲取屬性 (0 in this && this[0].nodeType == 1 && (result = this[0].getAttribute(name)) != null ? result : undefined) : // 設(shè)置屬性 this.each(function (idx) { if (this.nodeType !== 1) return // 設(shè)置多個(gè)屬性值 if (isObject(name)) for (key in name) setAttribute(this, key, name[key]) // 設(shè)置一個(gè)屬性值 else setAttribute(this, name, funcArg(this, value, idx, this.getAttribute(name))) }) }
代碼分為兩部分,獲取與設(shè)置屬性。先看
獲取部分
typeof name == "string" && !(1 in arguments)) ? // 獲取屬性 (0 in this && this[0].nodeType == 1 && (result = this[0].getAttribute(name)) != null ? result : undefined) : "設(shè)置代碼邏輯代碼塊"
當(dāng)name參數(shù)是string類型,并且沒有傳入value參數(shù)時(shí)候,意味著是讀取屬性的情況。緊接著再看當(dāng)前選中的元素集合中第一個(gè)元素是否存在并且節(jié)點(diǎn)類型是否為element類型,如果是,再調(diào)用getAttribute獲取name屬性,結(jié)果不為null或者undefined的話直接返回,否則統(tǒng)一返回undefined。
設(shè)置部分
this.each(function (idx) { if (this.nodeType !== 1) return // 設(shè)置多個(gè)屬性值 if (isObject(name)) for (key in name) setAttribute(this, key, name[key]) // 設(shè)置一個(gè)屬性值 else setAttribute(this, name, funcArg(this, value, idx, this.getAttribute(name))) })
調(diào)用each方法,對(duì)當(dāng)前的元素集合進(jìn)行遍歷操作,遍歷過程中,如果當(dāng)前的元素不是element類型,直接return掉。否則根據(jù)name參數(shù)傳入的是否是對(duì)象進(jìn)行兩個(gè)分支的操作。
如果name是個(gè)對(duì)象,那對(duì)對(duì)象進(jìn)行遍歷,再挨個(gè)調(diào)用setAttribute方法,進(jìn)行屬性設(shè)置操作。
不是對(duì)象的話,接下來的這行代碼,讓第二個(gè)參數(shù)既可以傳入普通的字符串,也可以傳入回調(diào)函數(shù)。
看個(gè)實(shí)際使用的例子
$(".box").attr("name", "qianlongo") $(".box").attr("name", function (idx, oldVal) { return oldVal + "qianlongo" })
可以看到如果傳入的是回調(diào)函數(shù),那回調(diào)函數(shù)可以接收到元素的索引,以及要設(shè)置的屬性的之前的值。
removeAttr()移除當(dāng)前對(duì)象集合中所有元素的指定屬性,理論上講attr也可以做到removeAttr的功能。只要將要移除的name屬性設(shè)置為null或者undefined即可。
removeAttr: function (name) { return this.each(function () { // 通過將name分割,再將需要移除的屬性進(jìn)行遍歷刪除 this.nodeType === 1 && name.split(" ").forEach(function (attribute) { setAttribute(this, attribute) }, this) }) }
代碼本身很簡(jiǎn)單,對(duì)當(dāng)前選中的元素集合進(jìn)行遍歷操作,然后對(duì)name參數(shù)進(jìn)行空格分割(這樣對(duì)于name傳入類似"name sex age"就可以批量刪除了),最后還是調(diào)用的setAttribute方法進(jìn)行屬性刪除操作。
prop()讀取或設(shè)置dom元素的屬性值,簡(jiǎn)寫或小寫名稱,比如for, class, readonly及類似的屬性,將被映射到實(shí)際的屬性上,比如htmlFor, className, readOnly, 等等。
直接看源碼實(shí)現(xiàn)
prop: function (name, value) { name = propMap[name] || name return (1 in arguments) ? this.each(function (idx) { this[name] = funcArg(this, value, idx, this[name]) }) : (this[0] && this[0][name]) }
通過1 in arguments作為設(shè)置與獲取元素屬性的判斷標(biāo)志,value傳了,則對(duì)當(dāng)前選中的元素集合進(jìn)行遍歷操作,同樣用到了funcArg函數(shù),讓value既可以傳入函數(shù),也可以傳入其他值。與attr方法不同的是,因?yàn)槭窃O(shè)置和獲取元素的固有屬性,所以直接向元素設(shè)置和讀取值就可以了。
需要注意的是當(dāng)你傳入class,for等屬性的時(shí)候需要被映射到className,htmlFor等,下面是映射列表
var propMap = { "tabindex": "tabIndex", "readonly": "readOnly", "for": "htmlFor", "class": "className", "maxlength": "maxLength", "cellspacing": "cellSpacing", "cellpadding": "cellPadding", "rowspan": "rowSpan", "colspan": "colSpan", "usemap": "useMap", "frameborder": "frameBorder", "contenteditable": "contentEditable" }removeProp()
從集合的每個(gè)DOM節(jié)點(diǎn)中刪除一個(gè)屬性
removeProp: function (name) { name = propMap[name] || name return this.each(function () { delete this[name] }) }
直接通過delete去刪除,但是如果嘗試刪除DOM的一些內(nèi)置屬性,如className或maxLength,將不會(huì)有任何效果,因?yàn)闉g覽器禁止刪除這些屬性。
html()獲取或設(shè)置對(duì)象集合中元素的HTML內(nèi)容。當(dāng)沒有給定content參數(shù)時(shí),返回對(duì)象集合中第一個(gè)元素的innerHtml。當(dāng)給定content參數(shù)時(shí),用其替換對(duì)象集合中每個(gè)元素的內(nèi)容。content可以是append中描述的所有類型zeptojs_api/#html
源碼分析
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) }
如果html傳了,就遍歷通過append函數(shù)設(shè)置html,沒傳就是獲取(即返回當(dāng)前集合的第一個(gè)元素的innerHTML)注意:這里的html參數(shù)可以是個(gè)函數(shù),接收的參數(shù)是當(dāng)前元素的索引和html。
text()獲取或者設(shè)置所有對(duì)象集合中元素的文本內(nèi)容。
當(dāng)沒有給定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,與它不同的是它不能用來獲取或設(shè)置 HTMLtext
text() ? string
text(content) ? self
text(function(index, oldText){ ... }) ? self
源碼分析
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) }
同樣包括設(shè)置和獲取兩部分,判斷的邊界則是是否傳入了第一個(gè)參數(shù)。先看獲取部分。
獲取text
(0 in this ? this.pluck("textContent").join("") : null)
0 in this 當(dāng)前是否選中了元素,沒有直接返回null,有則通過this.pluck("textContent").join("")獲取,我們先來看一下pluck做了些什么
plunck
// `pluck` is borrowed from Prototype.js pluck: function (property) { return $.map(this, function (el) { return el[property] }) },
pluck也是掛在原型上的方法之一,通過使用map方法遍歷當(dāng)前的元素集合,返回結(jié)果是一個(gè)數(shù)組,數(shù)組的每一項(xiàng)則是元素的property屬性。所以上面才通過join方法再次轉(zhuǎn)成了字符串。
還有一點(diǎn)需要注意的是text方法設(shè)置或者獲取都是在操作元素的textContent屬性,那它和innerText,innerHTML的區(qū)別在哪呢?可以查看MDN
設(shè)置text
this.each(function (idx) { var newText = funcArg(this, text, idx, this.textContent) this.textContent = newText == null ? "" : "" + newText })
設(shè)置與html的設(shè)置部分比較類似,既支持直接傳入普通的字符串也支持傳入回調(diào)函數(shù)。如果得到的newText為null或者undefined,會(huì)統(tǒng)一轉(zhuǎn)成空字符串再進(jìn)行設(shè)置。
val獲取或設(shè)置匹配元素的值。當(dāng)沒有給定value參數(shù),返回第一個(gè)元素的值。如果是標(biāo)簽,則返回一個(gè)數(shù)組。當(dāng)給定value參數(shù),那么將設(shè)置所有元素的值。val
val() ? string
val(value) ? self
val(function(index, oldValue){ ... }) ? self
以上是基本用法
源碼分析
val: function (value) { if (0 in arguments) { if (value == null) value = "" return this.each(function (idx) { this.value = funcArg(this, value, idx, this.value) }) } else { return this[0] && (this[0].multiple ? $(this[0]).find("option").filter(function () { return this.selected }).pluck("value") : this[0].value) } }
html,text和val方法對(duì)待取值和設(shè)置值的套路基本都是一樣的,判斷有沒有傳入第一個(gè)參數(shù),有則認(rèn)為是設(shè)置,沒有就是讀取。
先看讀取部分
return this[0] && (this[0].multiple ? $(this[0]).find("option").filter(function () { return this.selected }).pluck("value") : this[0].value)
假設(shè)this[0](也就是元素集合中第一個(gè)元素存在)我們把它拆成兩個(gè)部分來學(xué)習(xí)
獲取多選下拉列表的value
普通表單元素value
this[0].multiple ? "獲取多選下拉列表的value" : "普通表單元素value"
針對(duì)第一種情況首先會(huì)通過find函數(shù)取查找子元素option集合,然后再過this.selected過濾出已經(jīng)選中的option元素?cái)?shù)組,最后還是通過調(diào)用pluck函數(shù)返回該option元素集合中的value數(shù)組。
第二種情況則是直接讀取元素的value屬性即可。
接下來我們回去繼續(xù)看設(shè)置部分
if (value == null) value = "" return this.each(function (idx) { this.value = funcArg(this, value, idx, this.value) })
與html,text等方法類似,通過調(diào)用funcArg方法使得既支持普通字符串設(shè)置,也支持傳入回調(diào)函數(shù)返回值設(shè)置值。
data讀取或?qū)懭雂om的 data-* 屬性。行為有點(diǎn)像 attr ,但是屬性名稱前面加上 data-。#data
data(name) ? value
data(name, value) ? self
注意:data方法本質(zhì)上也是借用attr方法去實(shí)現(xiàn)的,不同之處在于data設(shè)置或者讀取的屬性為data-打頭。
源碼分析
data: function (name, value) { var attrName = "data-" + name.replace(capitalRE, "-$1").toLowerCase() var data = (1 in arguments) ? this.attr(attrName, value) : this.attr(attrName) return data !== null ? deserializeValue(data) : undefined },
data方法源碼分為三個(gè)部分
將傳入的name屬性轉(zhuǎn)化為data-開頭的連字符
通過attr方法設(shè)置或者獲取屬性
對(duì)attr方法的返回值再做一層映射處理
我們分別一一解釋一下這幾個(gè)部分
var capitalRE = /([A-Z])/g var attrName = "data-" + name.replace(capitalRE, "-$1").toLowerCase()
將小駝峰書寫形式轉(zhuǎn)換成以data-開頭的連字符形式,例如zeptoAnalysis => data-zepto-analysis
第二部分調(diào)用attr方法去設(shè)置后者獲取元素的屬性
第三部分挺有意思的,讀取屬性值時(shí),會(huì)有下列轉(zhuǎn)換:
“true”, “false”, and “null” 被轉(zhuǎn)換為相應(yīng)的類型;
數(shù)字值轉(zhuǎn)換為實(shí)際的數(shù)字類型;
JSON值將會(huì)被解析,如果它是有效的JSON;
其它的一切作為字符串返回。
來看一下這些轉(zhuǎn)轉(zhuǎn)操作是如何通過deserializeValue方法完成的。
function deserializeValue(value) { try { return value ? value == "true" || (value == "false" ? false : value == "null" ? null : +value + "" == value ? +value : /^[[{]/.test(value) ? $.parseJSON(value) : value) : value } catch (e) { return value } }
這個(gè)函數(shù)用的三元表達(dá)式比較復(fù)雜,一步步解析如下
如果value存在,則進(jìn)行第2步,否則直接返回value
當(dāng)value為字符串”true“時(shí),返回true,否則進(jìn)行第3步
當(dāng)value為字符串“false”時(shí),返回false,否則進(jìn)行第4步
當(dāng)value為字符串“null”時(shí),返回null,否則進(jìn)行第5步
當(dāng)value為類似“12”這種類型字符串時(shí),返回12(注意:+"12" => 12, +"01" => 1),否則進(jìn)行第6步
當(dāng)value以{或者[為開頭時(shí),使用parseJSON解析(但是有點(diǎn)不嚴(yán)格,因?yàn)橐詛[開頭不一定就是對(duì)象字符串),否則直接返回value
最后還有一個(gè)問題,不知道大家有沒有注意到zepto模塊中的data方法和data模塊中的data方法都是掛載到原型下面的,那他們之間到底有什么關(guān)系呢?可以查看之前寫的一篇文章Zepto中數(shù)據(jù)緩存原理與實(shí)現(xiàn) ,應(yīng)該可以找到答案
結(jié)尾參考以上是Zepto中常見的操作元素屬性的方法(attr、removeAttr、prop、removeProp、html、text、val、data)解析。歡迎指正其中的問題。
讀Zepto源碼之屬性操作
textContent mdn
multiple
zepto.js 源碼解析
文章記錄ie模塊
Zepto源碼分析之ie模塊(2017-11-03)
data模塊
Zepto中數(shù)據(jù)緩存原理與實(shí)現(xiàn)(2017-10-03)
form模塊
zepto源碼分析之form模塊(2017-10-01)
zepto模塊
這些Zepto中實(shí)用的方法集(2017-08-26)
Zepto核心模塊之工具方法拾遺 (2017-08-30)
看zepto如何實(shí)現(xiàn)增刪改查DOM (2017-10-2)
Zepto這樣操作元素屬性(2017-11-13)
event模塊
mouseenter與mouseover為何這般糾纏不清?(2017-06-05)
向zepto.js學(xué)習(xí)如何手動(dòng)觸發(fā)DOM事件(2017-06-07)
誰說你只是"會(huì)用"jQuery?(2017-06-08)
ajax模塊
原來你是這樣的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/90300.html
摘要:有一個(gè)模塊,專門用來做數(shù)據(jù)緩存,允許我們存放任何與相關(guān)的數(shù)據(jù)。在匹配元素上存儲(chǔ)任意相關(guān)數(shù)據(jù)或返回匹配的元素集合中的第一個(gè)元素的給定名稱的數(shù)據(jù)存儲(chǔ)的值。確定元素是否有與之相關(guān)的數(shù)據(jù)。 前言 以前我們使用Zepto進(jìn)行開發(fā)的時(shí)候,會(huì)把一些自定義的數(shù)據(jù)存到dom節(jié)點(diǎn)上,好處是非常直觀和便捷,但是也帶來了例如直接將數(shù)據(jù)暴露出來會(huì)出現(xiàn)安全問題,數(shù)據(jù)以html自定義屬性標(biāo)簽存在,對(duì)于瀏覽器本身來說...
摘要:的模塊用來獲取節(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 模塊用來獲取 DOM 節(jié)點(diǎn)中的 data-* 屬性的數(shù)據(jù),和儲(chǔ)存跟 DOM 相關(guān)的數(shù)據(jù)。 讀 Zepto 源碼系列文章已經(jīng)放到了github上,歡...
摘要:獲得當(dāng)前元素相對(duì)于的位置。返回一個(gè)對(duì)象含有和當(dāng)給定一個(gè)含有和屬性對(duì)象時(shí),使用這些值來對(duì)集合中每一個(gè)元素進(jìn)行相對(duì)于的定位。獲取對(duì)象集合中第一個(gè)元素相對(duì)于其的位置。結(jié)尾以上就是中與偏移相關(guān)的幾個(gè)的解析,歡迎指出其中的問題和有錯(cuò)誤的地方。 前言 這篇文章主要想說一下Zepto中與偏移相關(guān)的一些事,很久很久以前,我們經(jīng)常會(huì)使用offset、position、scrollTop、scrollLe...
閱讀 5108·2021-10-13 09:39
閱讀 1996·2019-08-29 11:12
閱讀 1185·2019-08-28 18:16
閱讀 1899·2019-08-26 12:16
閱讀 1283·2019-08-26 12:13
閱讀 3024·2019-08-26 10:59
閱讀 2346·2019-08-23 18:27
閱讀 3025·2019-08-23 18:02