jQuery源码分析系列:属性操作
属性操作
1.6.1相对1.5.x最大的改进,莫过于对属性.attr()的重写了。在1.6.1中,将.attr()一分为二: .attr()、.prop(),这是一个令人困惑的变更,也是一个破坏性的升级,会直接影响到无数的网站和项目升级到1.6。
简单的说,.attr()是通过setAttribute、getAttribute实现,.prop()则通过Element[ name ]实现:
jQuery.attr |
setAttribute, getAttribute |
jQuery.removeAttr |
removeAttribute, removeAttributeNode(getAttributeNode ) |
jQuery.prop |
Element[ name ] |
jQuery.removeProp |
delete Element[ name ] |
事实上.attr()和.prop()的不同,是HTML属性(HTML attributes)和DOM属性(DOM properties)的不同。HTML属性解析的是HTML代码中的存在的属性,返回的总是字符串,而DOM属性解析的是DOM对象的属性,可能是字符串,也可能是一个对象,可能与HTML属性相同,也可能不同。
1 <a href="abc.html" class="csstest" style="font-size: 30px;">link</a> 2 3 <input type="text" value="123"> 4 5 <input type="checkbox" checked="checked">
javascript代码:
1 console.info( $('#a').attr('href') ); // abc.html 2 3 console.info( $('#a').prop('href') ); // file:///H:/open/ws-nuysoft/com.jquery/jquery/abc.html 4 5 6 console.info( $('#a').attr('class') ); // csstest 7 console.info( $('#a').prop('class') ); // csstest 8 9 console.info( document.getElementById('a').getAttribute('class') ); // csstest 10 11 console.info( document.getElementById('a').className ); // csstest 12 13 console.info( $('#a').attr('style') ); // font-size: 30px; 14 15 console.info( $('#a').prop('style') ); // CSSStyleDeclaration { 0="font-size", fontSize="30px", ...} 16 17 console.info( document.getElementById('a').getAttribute('style') ); // font-size: 30px; 18 19 console.info( document.getElementById('a').style ); // CSSStyleDeclaration { 0="font-size", fontSize="30px", ...} 20 21 console.info( $('#text').attr('value') ); // 123 22 console.info( $('#text').prop('value') ); // 123 23 24 console.info( $('#checkbox').attr('checked') ); // checked 25 console.info( $('#checkbox').prop('checked') ); // true 26 27
不同之处总结如下:
1.属性名可能不同,尽管大部分的属性名还是相似或一致的。
2.HTML属性值总是返回字符串,DOM属性值则可能是整型、字符串、对象,可以获取更多的内容
3.DOM属性总是返回当前的状态(值),而HTML属性(在大多数浏览)返回的初始化时的状态(值)
4.DOM属性只能返回固定属性名的值,而HTML属性则可以返回在HTML代码中自定义的属性名的值
5.相对于HTML属性的浏览器兼容问题,DOM属性名和属性值在浏览器之间的差异更小,并且DOM属性也有标准可依
优先使用.prop(),因为.prop()总是返回最新的状态(值)
从源码可以看出.attr()的处理过程,先特殊处理各种特殊情况,再用约定getAttribute()和setAttribute()方法
jQuery.attr()源码:
.attr(attributeName) 取得第一个匹配元素的属性值(当属性没有被设置时,返回undefined,不能用在文本节点、注释节点、属性节点上)
.attr(attributeName, value) 设置单个属性
.attr(map) 设置多个属性
.attr(attributeName, function(index, attr)) 通过函数的返回值设置属
1 //工具方法 设置或获取HTML元素 setAttribute和getAttribute实现 2 attr: function( elem, name, value, pass ) { 3 var nType = elem.nodeType; 4 //节点类型不能是:文本 注释 属性节点 5 if(!elem || nType === 3 || nType === 8 nType === 2){ 6 return undefined; 7 } 8 //1>>遇到与方法同名的属性 则执行方法 9 //2>>遇到扩展或需要修正的属性 执行相应的方法 10 //判断属性名是否在jQuery提供的方法jQuery.attrFn中,是则直接调用方法。 11 if(pass && name in jQuery.attrFn){//属性方法 12 return jQuery(elem)[name](value); 13 } 14 //如果不支持getAttribute 则调用$.prop()方法 15 if(!("getAttribute" in elem)){ 16 return jQuery.prop(elem,name,value); 17 } 18 var ret,hooks, 19 notxml = nType !==1 || !jQuery.isXMLDoc(elem);//判断documentElement是否存在 20 21 //格式化name attrFix: { tabindex: "tabIndex"} 22 name = notxml && jQuery.attrFix[name] || name; 23 //属性钩子:type: tabIndex 24 hooks = jQuery.attrHooks[name]; 25 //如果没有name对应的钩子 26 if(!hooks){ 27 //使用boolean钩子处理boolean属性 28 (typeof value === "boolean" || value === undefined || value.tolowerCase() === name.toLowerCase())){ 29 //使用布尔钩子(静态方法对象):set get 30 hooks = boolHook; 31 //使用表单钩子 32 }else if (formHook && (jQuery.nodeName(elem,"form") || rinvalidChar.test(name))){ 33 //使用表单钩子(静态方法对象):set get 34 hooks = formHook; 35 } 36 } 37 38 //定义了value 设置或删除 39 if(value !== undefined){ 40 /* 41 typeof null === 'object' true 42 typeof undefined === 'undefined' true 43 null == undefined true 44 null === undefined false 45 */ 46 if(value === null){//有值但是为空 即将值设置为空 47 jQuery.reomveAttr(elem,name); 48 return undefined; 49 //属性钩子 布尔钩子 表单钩子 如果有对象的钩子 就调用set方法 50 }else if(hooks && "set" in hooks && noxml && (ret = hooks.set(elem,value,name))!==undefeind){ 51 return ret; 52 }else{ 53 //调用setAttribute方法 54 elem.setAttribute(name,"" + value); 55 return value; 56 } 57 //value是undefined,说明去属性 ,存在对应钩子有get方法,调用钩子的get方法 58 }else if(hooks && "get" in hooks && notxml){ 59 return hooks.get(elem,name); 60 }else{ 61 //取属性值 62 ret = elem.getAttribute(name); 63 //属性不存返回null 格式化为undefined 64 return ret === null ? undefined : ret; 65 } 66 },
其中调用的钩子有些不明白的地方:
attrHooks源码:如果name = type属性,则加判断后调用setAttribute设置属性值,或者name = tabIndex时通过getAttributeNode("tabIndex")获得//获得自定义对象属性.
//属性钩子 name = type || tabIndex attrHooks: { type: { set: function( elem, value ) { //type属性在IE下不能改变 如果改变报错 if ( rtype.test( elem.nodeName ) && elem.parentNode ) { jQuery.error( "type property can't be changed" ); } else if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) { var val = elem.value; elem.setAttribute( "type", value );//最后调用setAttribute设置type属性的值 if ( val ) { elem.value = val; } return value; } } }, tabIndex: { get: function( elem ) { //获得tabIndex的值 var attributeNode = elem.getAttributeNode("tabIndex");//获得自定义对象属性 return attributeNode && attributeNode.specified ? parseInt( attributeNode.value, 10 ) ://获得自定义对象属性的值 rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ? 0 : undefined; } } },
AttrFn源码:如果name在这个属性方法的对象中,直接调用这个属性方法。
//取属性用到的方法 attrFn: { val: true, css: true, html: true, text: true, data: true, width: true, height: true, offset: true },
boolHook源码:
1 //bool钩子 2 boolHook = { 3 get: function( elem, name ) { 4 // Align boolean attributes with corresponding properties 5 return elem[ jQuery.propFix[ name ] || name ] ? 6 name.toLowerCase() : 7 undefined; 8 }, 9 set: function( elem, value, name ) { 10 var propName; 11 if ( value === false ) {//value不存在时,删除boolean属性 12 jQuery.removeAttr( elem, name ); 13 } else { 14 //属性名 是否在propFix中,存在就直接调用 15 propName = jQuery.propFix[ name ] || name; 16 if ( propName in elem ) { 17 elem[ propName ] = value; 18 } 19 //propName不在elem中就用setAttribute 20 elem.setAttribute( name, name.toLowerCase() ); 21 } 22 return name; 23 } 24 };
formHook源码:设置表单钩子,get/set。
$.prop()源码:
1 //获取DOM属性 2 prop: function( elem, name, value ) { 3 var nType = elem.nodeType; 4 //节点类型不能是:文本 注释 属性节点 5 if(!elem || nType === 3 || nType === 8 nType === 2){ 6 return undefined; 7 } 8 var ret,hooks, 9 notxml = nType !==1 || !jQuery.isXMLDoc(elem);//判断documentElement是否存在 10 11 //格式化name attrFix: { tabindex: "tabIndex"} 12 name = notxml && jQuery.attrFix[name] || name; 13 //属性钩子:name = type || tabIndex 14 hooks = jQuery.attrHooks[name]; 15 if(value !== undefined){ 16 //如果钩子存在set 调用set方法 17 if(hooks && "set" && (ret = hooks.set(elem,value,name))!==undefined){ 18 return ret; 19 }else{ 20 return (elem[name] = value); 21 } 22 //读取 23 }else{ 24 if(hooks && "get" && (ret = hooks.get(elem,value,name))!==undefined){ 25 return ret; 26 }else{ 27 return elem[name]; 28 } 29 } 30 },
jQuery中调用access方法实现jQuery.fn.attr和jQuery.fn.prop:
1 attr: function( name, value ) { 2 return jQuery.access( this, name, value, true, jQuery.attr ); 3 }, 4 5 prop: function( name, value ) { 6 return jQuery.access( this, name, value, true, jQuery.prop ); 7 }
$.access方法源码:
多功能函数:读取或设置集合的属性值;值为函数时会被执行
1>>elems:元素的集合,【collection】【类】数组
2>>key:属性名称,key的只为object时,会拆解key为key,value形式再次执行jQuery.access
3>>value:属性值
4>>exec:在属性值为function时是否对设置之前的value值执行函数(这里为true)
5>>fn:执行的函数
6>>pass:是否设置为jQuery对象的属性 attr时使用
用于fn:jQuery.fn.css(),jQuery.fn.attr(),jQueyr.fn.prop
return jQuery.access( this, name, value, true, function( elem, name, value ) {});
说明:主要用于参数的循环处理,为什么要这样做?待详细分析
access:function(elems,key,value,exec,fn,pass){ var elems = elems.length; //如果有多个属性就迭代 if(typeof key === "object"){ for(var k in key){//key的参数循环处理 jQuery.access(elems,k,key[k],exec,fn,value); } return elems; } //只设置一个属性 if(value !== undefined){ exec = !pass && exec && jQuery.isFunction(value); for (var i=0;i<length;i++){ fn(elems[i],key, exec ? value.call(elems[i],i,fn(elems[i],key)) : value,pass); } return elems; } //读取属性 return length ? fn(elems[0],key) : undefined; },
posted on 2014-06-22 15:03 color_story 阅读(178) 评论(0) 编辑 收藏 举报