前言

使用Zepto的时候,我们经常会要去操作一些DOM的属性,或元素本身的固有属性或自定义属性等。比如常见的有attr(),removeAttr(),prop(),removeProp(),data()等。接下来我们挨个整明白他们是如何实现的...点击zepto模块源码注释查看这篇文章对应的解析。

attr()

读取或设置dom的属性。

如果没有给定value参数,则读取对象集合中第一个元素的属性值。

当给定了value参数。则设置对象集合中所有元素的该属性的值。当value参数为null,那么这个属性将被移除(类似removeAttr),多个属性可以通过对象键值对的方式进行设置。zeptojs_api/#attr

示例

// 获取name属性

attr(name)

// 设置name属性

attr(name, value)

// 设置name属性,不同的是使用回调函数的形式

attr(name, function(index, oldValue){ ... })

// 设置多个属性值

attr({ name: value, name2: value2, ... })

已经知道了如何使用attr方法,在开始分析attr实现源码之前,我们先了解一下这几个函数。

setAttribute

function setAttribute(node, name, value) {

value == null ? node.removeAttribute(name) : node.setAttribute(name, value)

}

它的主要作用就是设置或者删除node节点的属性。当value为null或者undefined的时候,调用removeAttribute方法移除name属性,否则调用setAttribute方法设置name属性。

funcArg

function funcArg(context, arg, idx, payload) {

return isFunction(arg) ? arg.call(context, idx, payload) : arg

}

funcArg函数在多个地方都有使用到,主要为类似attr,prop,val等方法中第二个参数可以是函数或其他类型提供可能和便捷。

如果传入的arg参数是函数类型,那么用context作为arg函数的执行上下文,以及将idx和payload作为参数去执行。否则直接返回arg参数。

好啦接下来开始看attr的源码实现了

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) :

// 设置属性

this.each(function (idx) {

if (this.nodeType !== 1) return

// 设置多个属性值

if (isObject(name)) for (key in name) setAttribute(this, key, name[key])

// 设置一个属性值

else setAttribute(this, name, funcArg(this, value, idx, this.getAttribute(name)))

})

}

代码分为两部分,获取与设置属性。先看

获取部分

typeof name == 'string' && !(1 in arguments)) ?

// 获取属性

(0 in this && this[0].nodeType == 1 && (result = this[0].getAttribute(name)) != null ? result : undefined) : '设置代码逻辑代码块'

当name参数是string类型,并且没有传入value参数时候,意味着是读取属性的情况。紧接着再看当前选中的元素集合中第一个元素是否存在并且节点类型是否为element类型,如果是,再调用getAttribute获取name属性,结果不为null或者undefined的话直接返回,否则统一返回undefined。

设置部分

this.each(function (idx) {

if (this.nodeType !== 1) return

// 设置多个属性值

if (isObject(name)) for (key in name) setAttribute(this, key, name[key])

// 设置一个属性值

else setAttribute(this, name, funcArg(this, value, idx, this.getAttribute(name)))

})

调用each方法,对当前的元素集合进行遍历操作,遍历过程中,如果当前的元素不是element类型,直接return掉。否则根据name参数传入的是否是对象进行两个分支的操作。

如果name是个对象,那对对象进行遍历,再挨个调用setAttribute方法,进行属性设置操作。

不是对象的话,接下来的这行代码,让第二个参数既可以传入普通的字符串,也可以传入回调函数。

看个实际使用的例子

$('.box').attr('name', 'qianlongo')

$('.box').attr('name', function (idx, oldVal) {

return oldVal + 'qianlongo'

})

可以看到如果传入的是回调函数,那回调函数可以接收到元素的索引,以及要设置的属性的之前的值。

removeAttr()

移除当前对象集合中所有元素的指定属性,理论上讲attr也可以做到removeAttr的功能。只要将要移除的name属性设置为null或者undefined即可。

removeAttr: function (name) {

return this.each(function () {

// 通过将name分割,再将需要移除的属性进行遍历删除

this.nodeType === 1 && name.split(' ').forEach(function (attribute) {

setAttribute(this, attribute)

}, this)

})

}

代码本身很简单,对当前选中的元素集合进行遍历操作,然后对name参数进行空格分割(这样对于name传入类似'name sex age'就可以批量删除了),最后还是调用的setAttribute方法进行属性删除操作。

prop()

读取或设置dom元素的属性值,简写或小写名称,比如for, class, readonly及类似的属性,将被映射到实际的属性上,比如htmlFor, className, readOnly, 等等。

直接看源码实现

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作为设置与获取元素属性的判断标志,value传了,则对当前选中的元素集合进行遍历操作,同样用到了funcArg函数,让value既可以传入函数,也可以传入其他值。与attr方法不同的是,因为是设置和获取元素的固有属性,所以直接向元素设置和读取值就可以了。

需要注意的是当你传入class,for等属性的时候需要被映射到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()

从集合的每个DOM节点中删除一个属性

removeProp: function (name) {

name = propMap[name] || name

return this.each(function () { delete this[name] })

}

直接通过delete去删除,但是如果尝试删除DOM的一些内置属性,如className或maxLength,将不会有任何效果,因为浏览器禁止删除这些属性。

html()

获取或设置对象集合中元素的HTML内容。当没有给定content参数时,返回对象集合中第一个元素的innerHtml。当给定content参数时,用其替换对象集合中每个元素的内容。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函数设置html,没传就是获取(即返回当前集合的第一个元素的innerHTML)注意:这里的html参数可以是个函数,接收的参数是当前元素的索引和html。

text()

获取或者设置所有对象集合中元素的文本内容。

当没有给定content参数时,返回当前对象集合中第一个元素的文本内容(包含子节点中的文本内容)。

当给定content参数时,使用它替换对象集合中所有元素的文本内容。它有待点似 html,与它不同的是它不能用来获取或设置 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)

}

同样包括设置和获取两部分,判断的边界则是是否传入了第一个参数。先看获取部分。

获取text

(0 in this ? this.pluck('textContent').join("") : null)

0 in this 当前是否选中了元素,没有直接返回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方法遍历当前的元素集合,返回结果是一个数组,数组的每一项则是元素的property属性。所以上面才通过join方法再次转成了字符串。

还有一点需要注意的是text方法设置或者获取都是在操作元素的textContent属性,那它和innerText,innerHTML的区别在哪呢?可以查看MDN

设置text

this.each(function (idx) {

var newText = funcArg(this, text, idx, this.textContent)

this.textContent = newText == null ? '' : '' + newText

})

设置与html的设置部分比较类似,既支持直接传入普通的字符串也支持传入回调函数。如果得到的newText为null或者undefined,会统一转成空字符串再进行设置。

val

获取或设置匹配元素的值。当没有给定value参数,返回第一个元素的值。如果是标签,则返回一个数组。当给定value参数,那么将设置所有元素的值。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方法对待取值和设置值的套路基本都是一样的,判断有没有传入第一个参数,有则认为是设置,没有就是读取。

先看读取部分

return this[0] && (this[0].multiple ?

$(this[0]).find('option').filter(function () { return this.selected }).pluck('value') :

this[0].value)

假设this[0](也就是元素集合中第一个元素存在)我们把它拆成两个部分来学习

获取多选下拉列表的value

普通表单元素value

this[0].multiple ? '获取多选下拉列表的value' : '普通表单元素value'

针对第一种情况首先会通过find函数取查找子元素option集合,然后再过this.selected过滤出已经选中的option元素数组,最后还是通过调用pluck函数返回该option元素集合中的value数组。

第二种情况则是直接读取元素的value属性即可。

接下来我们回去继续看设置部分

if (value == null) value = ""

return this.each(function (idx) {

this.value = funcArg(this, value, idx, this.value)

})

与html,text等方法类似,通过调用funcArg方法使得既支持普通字符串设置,也支持传入回调函数返回值设置值。

data

读取或写入dom的 data-* 属性。行为有点像 attr ,但是属性名称前面加上 data-。#data

data(name) ⇒ value

data(name, value) ⇒ self

注意:data方法本质上也是借用attr方法去实现的,不同之处在于data设置或者读取的属性为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方法源码分为三个部分

将传入的name属性转化为data-开头的连字符

通过attr方法设置或者获取属性

对attr方法的返回值再做一层映射处理

我们分别一一解释一下这几个部分

var capitalRE = /([A-Z])/g

var attrName = 'data-' + name.replace(capitalRE, '-$1').toLowerCase()

将小驼峰书写形式转换成以data-开头的连字符形式,例如zeptoAnalysis => data-zepto-analysis

第二部分调用attr方法去设置后者获取元素的属性

第三部分挺有意思的,读取属性值时,会有下列转换:

“true”, “false”, and “null” 被转换为相应的类型;

数字值转换为实际的数字类型;

JSON值将会被解析,如果它是有效的JSON;

其它的一切作为字符串返回。

来看一下这些转转操作是如何通过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

}

}

这个函数用的三元表达式比较复杂,一步步解析如下

如果value存在,则进行第2步,否则直接返回value

当value为字符串”true“时,返回true,否则进行第3步

当value为字符串“false”时,返回false,否则进行第4步

当value为字符串“null”时,返回null,否则进行第5步

当value为类似“12”这种类型字符串时,返回12(注意:+'12' => 12, +'01' => 1),否则进行第6步

当value以{或者[为开头时,使用parseJSON解析(但是有点不严格,因为以{[开头不一定就是对象字符串),否则直接返回value

最后还有一个问题,不知道大家有没有注意到zepto模块中的data方法和data模块中的data方法都是挂载到原型下面的,那他们之间到底有什么关系呢?可以查看之前写的一篇文章Zepto中数据缓存原理与实现,应该可以找到答案

结尾

以上是Zepto中常见的操作元素属性的方法(attr、removeAttr、prop、removeProp、html、text、val、data)解析。欢迎指正其中的问题。

参考

文章记录

ie模块

data模块

form模块

zepto模块

event模块

ajax模块

zepto获取html内容,Zepto这样操作元素属性相关推荐

  1. js 操作元素属性、操作元素样式、display(none、block)

    js操作元素属性 <!DOCTYPE html> <html lang="en"> <head><meta charset="U ...

  2. jQuery操作元素属性、jQuery循环

    jQuery操作元素属性.jQuery循环 1.prop() 取出或设置某个属性的值 2.html() 取出或设置html内容 3. 手风琴效果实现: 4.jQuery的循环: 1.prop() 取出 ...

  3. jQuery学习3:操作元素属性和特性

    特性属性:是指DOM元素中能够和HTML元素中某个特性对应得上的属性. 下面就列出jQuery中提供的方法: 操作元素属性:each(iterator)遍历包装集里所有元素,为各元素分别调用传递进来的 ...

  4. html添加删除元素属性,jQuery学习笔记——.attr()和.removeAttr()方法操作元素属性

    今天主要和大家一起分享一下如何使用jQuery的jQuery就可以让我们轻松的读取,添加,更改或删除一个(或多个)元素中的任何属性,在jQuery中我们可以使用下面的方法来实现:.attr():jQu ...

  5. JS获取、设置、移除元素属性的操作

  6. jQuery 操作元素属性

    规则:给值就是设置,不给值就是获取 attr(key,[val]):除了表单元素布尔值以外的属性都使用attr $("div").attr("id"," ...

  7. jQuery操作元素属性

    prop()方法 返回或设置被选元素的属性和值 当返回属性值时,则返回第一个匹配元素的值 当设置属性值时,则为匹配元素集合设置一个或多个属性/值对 语法格式 返回属性的值 $(selector).pr ...

  8. uniapp 选择元素,操作元素属性

    uni.createSelectorQuery().selectViewport().scrollOffset(res => {   console.log("竖直滚动位置" ...

  9. APIS——获取dom对象、操作元素内容和属性、间歇函数

    APIS 变量声明 Web API基本认知 作用和分类 DOM DOM树 DOM对象 获取DOM对象 根据CSS选择器来获取DOM元素(重点) 其它获取DOM元素方法(了解) 操作元素内容 操作元素属 ...

最新文章

  1. P1051 谁拿了最多奖学金
  2. 手机版网页设计注意事项
  3. Linux基础——gcc编译、静态库与动态库(共享库)
  4. python dict sorted 排序
  5. 希捷操作系统SeaOS工作原理
  6. 不同映射方式下cache的失效率_详解发动机在不同工况下的喷油量控制方式
  7. 竟然如此简单!C++实现完全随机加密、解析库,并附上完整代码分析
  8. linux 游戏 黑白,黑白迭代官方版游戏-黑白迭代下载v0.5-Linux公社
  9. 什么是好的虚拟化项目交付(转)
  10. Echarts数据可视化全解
  11. 带你了解开课吧,Python培训的怎么样?
  12. 企业如何实践开源协同
  13. c语言中lu是什么数据类型,C语言编程入门之--第四章C语言基本数据类型
  14. 柳如是,当得奇女子。
  15. “云适配”获1亿元B+轮融资,盯上了大企业的移动化需求
  16. 项目实战:Qt贝塞尔曲线拽托顶点实时显示工具
  17. 快学Scala 学习笔记-1: (第一章到第三章)
  18. 【进阶四】Python实现(MD)HVRP常见求解算法——蚁群算法(ACO)
  19. Kettle5.2 couldn't convert string [...] to a date using format [yyyy/MM/dd HH:mm:ss.SSS]
  20. SII-Slave Information Interface

热门文章

  1. 【刷题】JavaScript V8华为机试
  2. C++求一维数组中极大值
  3. Just Using Your Imagination.Come On!
  4. html5 drawimage参数,小程序中canvas的drawImage方法参数详解
  5. Windows 上建立虚拟机安装Centos系统并部署 Kubernates 集群
  6. excel一列求和_超级简单实用excel制作表格,手把手教学(适合无根基初学者)...
  7. 提升小目标检测新的包围框相似度度量:Normalized Gaussian Wasserstein Distance
  8. 网站升级维护nginx配置
  9. 2022全国职业院校技能大赛-网络系统管理-Ruijie网络模块-真题解析 1
  10. 第十二届蓝桥杯单片机完整程序