当前位置: 首页 > Web前端 > HTML5

Zepto是这样操作元素属性的

时间:2023-04-05 22:47:51 HTML5

前言在使用Zepto的时候,我们经常要操作一些DOM属性,或者是元素本身固有的或者自定义的属性。比如常见的有attr()、removeAttr()、prop()、removeProp()、data()等,接下来我们就一一了解它们是如何实现的……点击zepto模块源码注释查看本文的相应分析。链接原源码仓库attr()读取或设置dom的属性。如果没有给出值参数,则读取对象集合中第一个元素的属性值。当给出值参数时。然后为对象集合中的所有元素设置此属性的值。当value参数为null时,该属性将被移除(类似removeAttr),可以通过对象键值对设置多个属性。zeptojs_api/#attrexample//获取name属性attr(name)//设置name属性attr(name,value)//设置name属性,区别在于使用回调函数的形式attr(name,function(index,oldValue){...})//设置多个属性值attr({name:value,name2:value2,...})已经知道attr方法的使用方法,开始分析源码之前attr实现代码,让我们了解这几个功能。setAttributefunctionsetAttribute(node,name,value){value==null?node.removeAttribute(name):node.setAttribute(name,value)}主要作用是设置或删除node节点的属性。当值为null或undefined时,调用removeAttribute方法移除name属性,否则调用setAttribute方法设置name属性。funcArgfunctionfuncArg(context,arg,idx,payload){returnisFunction(arg)?arg.call(context,idx,payload):arg}funcArg函数用在很多地方,主要是attr,prop,val等方法中的第二个参数,可以是函数,也可以是其他类型,提供可能性和方便。如果传入的arg参数是函数类型,则使用context作为arg函数的执行上下文,使用idx和payload作为参数执行。否则直接返回arg参数。好了,我们开始看attr的源码,实现attr:function(name,value){varresultreturn(typeofname=='string'&&!(1inarguments))?//获取属性(0inthis&&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(keyinname)setAttribute(this,key,name[key])//SetanattributevalueelsesetAttribute(this,name,funcArg(this,value,idx,this.getAttribute(name)))})}代码分为两部分,获取和设置属性。先看获取部分typeofname=='string'&&!(1inarguments))?//获取属性(0inthis&&this[0].nodeType==1&&(result=this[0].getAttribute(name))!=null?result:undefined):'设置代码逻辑代码块'当name参数是string类型,没有传入value参数,表示读取的是属性。接下来检查当前选中的元素集中的第一个元素是否存在,节点类型是否为元素类型。如果是,则调用getAttribute获取名称属性。如果结果不为null或undefined,则直接返回,否则统一返回undefined。设置部分this.each(function(idx){if(this.nodeType!==1)return//设置多个属性值if(isObject(name))for(keyinname)setAttribute(this,key,name[key])//设置一个属性值elsesetAttribute(this,name,funcArg(this,value,idx,this.getAttribute(name)))})调用各个方法遍历当前元素集,在遍历过程中,如果当前元素不是元素类型,直接返回。否则根据name参数的输入是否为对象,进行两个分支的操作??。如果name是一个对象,则遍历该对象,然后逐个调用setAttribute方法进行属性设置操作。如果它不是对象,则下一行代码允许第二个参数以普通字符串或回调函数的形式传递。看一个实际例子$('.box').attr('name','qianlongo')$('.box').attr('name',function(idx,oldVal){returnoldVal+'qianlongo'})可以看到如果传入回调函数,回调函数可以接收到元素的索引和要设置的属性的前一个值。removeAttr()移除当前对象集合中所有元素的指定属性。理论上attr也可以实现removeAttr的功能。只需将要删除的name属性设置为null或undefined即可。removeAttr:function(name){returnthis.each(function(){//通过拆分name,然后遍历需要删除的属性删除this.nodeType===1&&name.split('').forEach(function(attribute){setAttribute(this,attribute)},this)})}代码本身非常简单。遍历当前选中的元素集合,然后将name参数用空格分割(这样传入的name像'namesexage'可以批量删除),最后调用setAttribute方法进行属性删除操作.prop()读取或设置dom元素的属性值,简写或小写的名称,如for、class、readonly等类似的属性,会映射到实际的属性,如htmlFor、className、readOnly等。直接看源码实现prop:function(name,value){name=propMap[name]||名称返回(参数中有1个)?this.each(function(idx){this[name]=funcArg(this,value,idx,this[name])}):(this[0]&&this[0][name])}在参数中使用1作为一个用于设置和获取元素属性的判断标志,如果传值,则当前选择的元素集将被遍历操作,同样使用funcArg函数,这样可以传值给函数或者其他值。不同于attr方法,因为是设置和获取元素的固有属性,所以直接给元素设置和读取值就可以了。需要注意的是,传入class、for等属性时,需要映射到className、htmlFor等,下面是映射列表varpropMap={'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:函数(名称){名称=propMap[名称]||namereturnthis.each(function(){deletethis[name]})}直接通过delete删除,但是如果你尝试删除一些DOM的内置属性,比如className或者maxLength,则不会有任何效果,因为浏览器禁止删除这些属性。html()获取或设置对象集合中元素的HTML内容。当没有给出内容参数时,返回对象集合中第一个元素的innerHtml。当给定一个内容参数时,用它替换对象集合中每个元素的内容。content可以是appendzeptojs_api/#html源码分析中描述的所有类型html:function(html){return0inarguments?this.each(function(idx){varoriginHtml=this.innerHTML$(this).empty().append(funcArg(this,html,idx,originHtml))}):(0inthis?this[0].innerHTML:null)}如果传入html,则通过append函数遍历设置html,如果没有传入,则获取(即返回当前集合第一个元素的innerHTML)注:这里的html参数可以是一个函数,接收的参数是当前元素的index和html。text()获取或设置对象集合中所有元素的文本内容。当不给出content参数时,返回当前对象集合中第一个元素的文本内容(包括子节点中的文本内容)。当给出一个内容参数时,它用于替换对象集合中所有元素的文本内容。有点像html,不同的是不能用来获取或设置(文本){在参数中返回0?this.each(function(idx){varnewText=funcArg(this,text,idx,this.textContent)this.textContent=newText==null?'':''+newText}):(0inthis?this.pluck('textContent').join(""):null)}也包括设置和获取,判断的边界是第一个参数是否传入。先看获取部分。获取text(0inthis?this.pluck('textContent').join(""):null)0inthis元素当前是否被选中,如果没有直接返回null,否则传this.pluck('textContent').join(""),看看pluck做了什么})},pluck也是挂在原型上的方法之一。使用map方法遍历当前元素集合,返回的结果是一个数组,数组的每一项都是元素的property属性。于是上面又通过join方法转成了字符串。另外需要注意的是,文本方法设置或获取是操作元素的textContent属性,那么它和innerText、innerHTML有什么区别呢?您可以检查MDN设置textthis.each(function(idx){varnewText=funcArg(this,text,idx,this.textContent)this.textContent=newText==null?'':''+newText})设置和html设置部分类似。它既支持直接输入普通字符串,也支持回调函数。如果获取到的newText为null或undefined,则统一转为空字符串再设置。val获取或设置匹配元素的值。当没有给出值参数时,返回第一个元素的值。如果是标签,则返回一个数组。当给出value参数时,将设置所有元素的值。valval()?stringval(value)?selfval(function(index,oldValue){...})?self以上是基本用法源码分析val:function(value){if(0inarguments){if(value==null)value=""returnthis.each(function(idx){this.value=funcArg(this,value,idx,this.value)})}else{returnthis[0]&&(this[0].multiple?$(this[0]).find('option').filter(function(){returnthis.selected}).pluck('value'):this[0].value)}}html,text和val方法对待获取值和设置值的套路基本相同,判断第一个参数是否传入,如果是则认为是设置,如果不是则读取。先看读取部分returnthis[0]&&(this[0].multiple?$(this[0]).find('option').filter(function(){returnthis.selected}).pluck('value'):this[0].value)假设this[0](即元素集合中的第一个元素存在)我们将其拆分为两部分来学习如何获取多选下拉的值-下列表。普通表单元素valuethis[0].multiple?'获取多选下拉列表的值':'普通表单元素值'对于第一种情况,会先使用find函数查找子元素选项集,然后使用this.selected进行过滤取出选中的options元素数组,最后调用pluck函数返回option元素集合中的value数组。第二种情况是直接读取元素的value属性。接下来我们回头继续看设置部分if(value==null)value=""returnthis.each(function(idx){this.value=funcArg(this,value,idx,this.value)})和html、Text等方法类似。通过调用funcArg方法,不仅支持普通字符串的设置,还支持回调函数的返回值设置值。data读取或写入DOM的data-*属性。行为有点像attr,但在属性名称前加上data-前缀。#datadata(name)?valuedata(name,value)?self注:data方法本质上是借用attr方法实现的,不同的是data设置或读取的属性以data-开头。源码分析数据:function(name,value){varattrName='data-'+name.replace(capitalRE,'-$1').toLowerCase()vardata=(1inarguments)?this.attr(attrName,value):this.attr(attrName)返回数据!==null?deserializeValue(data):undefined},data方法源码分为三部分将传入的name属性转换为data开头的连字符-通过attr方法设置或获取属性到attr的返回值方法,然后做一层映射。让我们一一解释这些部分。varcapitalRE=/([A-Z])/gvarattrName='data-'+name.replace(capitalRE,'-$1').toLowerCase()将小驼峰式转换为以data-开头的连字符形式,例如zeptoAnalysis=>data-zepto-analysis第二部分调用attr方法设置后者获取元素的属性和“null”被转换为相应的类型;数值转换为实际数值类型;JSON值如果是有效的JSON将被解析;其他所有内容都作为字符串返回。下面我们通过deserializeValue方法来看看这些传输操作是如何完成的。函数反序列化值(值){尝试{返回值?价值==“真”||(值==“假”?假:值==“空”?空:+值+“”==值?+值:/^[\[\{]/.test(值)?$.parseJSON(value):value):value}catch(e){returnvalue}}该函数使用的三元表达式比较复杂,需要一步步分析如果value存在则转第2步,否则返回value直接地。当值为字符串“true”时,返回true,否则转第3步。当值为字符串“false”时,返回false,否则转第4步当值为字符串“null”时,返回null,否则转到第5步。当值为"12"之类的字符串时,返回12(注意:+'12'=>12,+'01'=>1),否则转到第6步。当值以{或者[,用parseJSON解析(但是有点松,因为{[开头不一定是object字符串),否则直接返回值,最后还有一个问题,不知你注意到zepto模块中的data方法和data模块中的data方法是挂载在prototype下的,那么它们之间是什么关系呢?可以查看之前写过的一篇文章Zepto中数据缓存的原理和实现,应该可以找到答案。.欢迎指正问题。参考阅读Zepto源码属性操作textContentmdnmultiplezepto.js源码分析文章记录ie模块Zepto源码分析ie模块(2017-11-03)数据模块Zepto数据缓存原理及实现(2017-10-03)form模块zepto源码Analysisformmodule(2017-10-01)zepto模块,Zepto中的这些实用方法集合(2017-08-26)Zepto核心模块工具和方法合集(2017-08-30)看看zepto是如何实现增删改查的修改和查询DOM(2017-10-2)Zepto这样操作元素属性(2017-11-13)为什么event模块中的mouseenter和mouseover这么纠结?(2017-06-05)了解如何从zepto.js手动触发DOM事件(2017-06-07)谁说你只是“知道如何使用”jQuery?(2017-06-08)ajax模块原来你是这样的jsonp(原理及具体实现细节)(2017-06-11)