jQuery的属性操作主要包括

  jQuery.fn.val

  jQuery.fn.attr

  jQuery.fn.removeAttr

  jQuery.fn.prop

  jQuery.fn.removeProp

  jQuery.fn.addClass

  jQuery.fn.removeClass

  jQuery.fn.toggleClass

  

  接下来一一分析jQuery对他们的处理

a. jQuery.fn.val


  jQuery.fn.val用来获取jQuery对象的第一个元素的val值或者给jQuery对象的每一个元素设置其val值。参数个数为0表示获取get,否则表示设置set。

  处理过程比较简单:

  判断参数个数,没有参数表示获取当前匹配元素中的第一个元素的value值,此时如果能使用valHooks则使用之,否则使用elem.value获取值(null/undefined需要转成空字符"");

  如果有参数,表示要为当前所有的匹配元素设置值,如果参数是函数,调用函数的返回值作为值val,否则使用传递的参数做为值val。能使用则用之,否则使用elem.value = val。

  源码:

val: function( value ) {var ret, hooks, isFunction,elem = this[0];//获取jQuery对象的第一个元素//获取值if ( !arguments.length ) {if ( elem ) {hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ];//通过hooks如果能取到值则返回if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) {return ret;}//否则正常取值ret = elem.value;return typeof ret === "string" ?// 换行符转换成空字符ret.replace(rreturn, "") ://处理null/undef 或数字ret == null ? "" : ret;}return;}isFunction = jQuery.isFunction( value );//对jQuery对象的每一个元素设置valreturn this.each(function( i ) {var val,self = jQuery(this);//元素不为DOM节点则返回if ( this.nodeType !== 1 ) {return;}if ( isFunction ) {val = value.call( this, i, self.val() );} else {val = value;}//用空字符替换null/undefined;数字转化成字符串if ( val == null ) {val = "";} else if ( typeof val === "number" ) {val += "";} else if ( jQuery.isArray( val ) ) {val = jQuery.map(val, function ( value ) {return value == null ? "" : value + "";});}hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ];//如果hooks的set返回为undefined,则使用正常设置if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) {this.value = val;}});
}

View Code

b. jQuery.fn.attr  


  设置或返回当前jQuery对象所匹配的元素节点的属性值。

        attr: function( name, value ) {return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 );}

  当前匹配的元素挨个执行jQuery.attr,并返回执行结果集。

  $(...).attr的最基础api函数jQuery.attr。关键代码如下,其他省略。深度理解钩子机制。

notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
// All attributes are lowercase
// Grab necessary hook if one is defined
if ( notxml ) {name = name.toLowerCase();
  //查找hookhooks = jQuery.attrHooks[ name ] || ( rboolean.test( name ) ? boolHook : nodeHook );
}
if ( value !== undefined ) {if ( value === null ) {jQuery.removeAttr( elem, name );//如果有hooks,就使用hooks来处理} else if ( hooks && notxml && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
    return ret;} else {…}//如果有hooks,就使用hooks来处理
} else if ( hooks && notxml && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {
  return ret;
} else {…}

c. jQuery.fn.removeAttr


  用于移除在当前jQuery对象所匹配的每一个元素节点上指定的属性  

    removeAttr: function( name ) {return this.each(function() {jQuery.removeAttr( this, name );});}

  内部使用低级API jQuery.removeAttr实现

  底层使用elem.removeAttribute来实现。不过需要注意的是因为可能根本就没有要删除的属性,所以在删除之前都设置了该属性

                // Boolean attributes get special treatment (#10870)if ( rboolean.test( name ) ) {// 如果是bool类型的属性(attribute)设置相应的特性(property)//同时清除defaultChecked/defaultSelected (if appropriate) for IE<8if ( !getSetAttribute && ruseDefault.test( name ) ) {elem[ jQuery.camelCase( "default-" + name ) ] =elem[ propName ] = false;} else {elem[ propName ] = false;}// See #9699 for explanation of this approach (setting first, then removal)} else {jQuery.attr( elem, name, "" );}

  完整的jQuery.removeAttr源码如下

removeAttr: function( elem, value ) {var name, propName,i = 0,attrNames = value && value.match( core_rnotwhite );if ( attrNames && elem.nodeType === 1 ) {while ( (name = attrNames[i++]) ) {propName = jQuery.propFix[ name ] || name;// Boolean attributes get special treatment (#10870)if ( rboolean.test( name ) ) {// 如果是bool类型的属性(attribute)设置相应的特性(property)//同时清除defaultChecked/defaultSelected (if appropriate) for IE<8if ( !getSetAttribute && ruseDefault.test( name ) ) {elem[ jQuery.camelCase( "default-" + name ) ] = elem[ propName ] = false;} else {elem[ propName ] = false;}// See #9699 for explanation of this approach (setting first, then removal)} else {jQuery.attr( elem, name, "" );}elem.removeAttribute( getSetAttribute ? name : propName );}}
}

View Code

  

d. jQuery.fn.prop


  在分析这个函数之前先说一点必备知识。

  attribute和property的区别

  1)      property是对象的属性值(有的时候文章中也称为特征值,他们是相同的),通过elem[ name ]来取值/赋值; 而attribute是直接写在标签上的属性,通过elem.getAttribute /elem.setAttribute。观察一张图很直观的理解(引用Aaron的图例)

 attributes是一个类数组的容器,说得准确点就是NameNodeMap,总之就是一个类似数组但又和数组不太一样的容器。attributes的每个数字索引以名值对(name=”value”)的形式存放了一个attribute节点。attributes是会随着添加或删除attribute节点动态更新的。property就是一个属性,如果把DOM元素看成是一个普通的Object对象,那么property就是一个以名值对(name=”value”)的形式存放在Object中的属性。要添加和删除property也简单多了,和普通的对象没啥分别。之所以attribute和property容易混倄在一起的原因是,很多attribute节点还有一个相对应的property属性

  2)      boolen类型的attr值更改是通过prop(elem[ propName ] = false;)方式来处理的,因为properties就是浏览器用来记录当前值的东西,boolean properties保持最新。但相应的boolean attributes是不一样的,它们仅被浏览器用来保存初始值。

  jQuery.fn.prop内部使用低级API jQuery.prop实现

prop: function( name, value ) {return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 );},

  jQuery.prop的核心代码如下,其他代码省略

if ( notxml ) {
  // Fix name and attach hooksname = jQuery.propFix[ name ] || name;
  //查找hookhooks = jQuery.propHooks[ name ];
}
if ( value !== undefined ) {
  //如果有hooks,就使用hooks来处理
  if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
    return ret;} else {return ( elem[ name ] = value );}
} else {
  //如果有hooks,就使用hooks来处理
  if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {
    return ret;} else {
    return elem[ name ];}
}

e. jQuery.fn.removeProp


  jQuery.fn.removeProp比较简单,就如同普通的js对象一样删除某个属性直接使用delete即可

propFix: {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: function( name ) {name = jQuery.propFix[ name ] || name;return this.each(function() {//try/catch handles cases where IE balks (such as removing a property on window)try {//删除prop处理this[ name ] = undefined;delete this[ name ];} catch( e ) {}});}

f. jQuery.fn.addClass


  这个函数处理比较简单,将参数字符串使用空格分隔(多个class)为classes,将原来的ClassName获取出来为cur,将分classes添加到cur中即可(在此过程中需要保证classes[i]在cur中不存在即可)

  重点源码

               //使用空格符分隔参数          classes = ( value || "" ).match( core_rnotwhite ) || [];for ( ; i < len; i++ ) {elem = this[ i ];            //获取原来的class 名称cur = elem.nodeType === 1 && ( elem.className ?( " " + elem.className + " " ).replace( rclass, " " ) :" ");if ( cur ) {j = 0;              //class名称组合while ( (clazz = classes[j++]) ) {                //保证class名称的唯一性if ( cur.indexOf( " " + clazz + " " ) < 0 ) {cur += clazz + " ";}}elem.className = jQuery.trim( cur );}}

g. jQuery.fn.removeClass


  这个函数也比较简单了,这里不分析了

h. jQuery.fn.toggleClass


  这个函数只需要在调用.addClass和.removeClass之间切换即可。toggleClass因为可能需要来回切换的原因,需要保存原来的class,以便下次调用的时候恢复回来。不过需要注意的是当没有传递参数时,被认为是整个Class的切换,需要保存原来的class,以便下次调用的时候恢复回来,关键代码如下

// Toggle whole class name
else if ( type === core_strundefined || type === "boolean" ) {if ( this.className ) {jQuery._data( this, "__className__", this.className );}this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || "";
}

  完整源码  

        toggleClass: function( value, stateVal ) {var type = typeof value,isBool = typeof stateVal === "boolean";if ( jQuery.isFunction( value ) ) {return this.each(function( i ) {jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal );});}return this.each(function() {if ( type === "string" ) {// toggle individual class namesvar className,i = 0,self = jQuery( this ),state = stateVal,classNames = value.match( core_rnotwhite ) || [];while ( (className = classNames[ i++ ]) ) {// check each className given, space separated liststate = isBool ? state : !self.hasClass( className );self[ state ? "addClass" : "removeClass" ]( className );}// Toggle whole class name} else if ( type === core_strundefined || type === "boolean" ) {if ( this.className ) {// store className if setjQuery._data( this, "__className__", this.className );}// If the element has a class name or if we're passed "false",// then remove the whole classname (if there was one, the above saved it).// Otherwise bring back whatever was previously saved (if anything),// falling back to the empty string if nothing was stored.this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || "";}});},

View Code

  如果觉得本文不错,请点击右下方【推荐】!

jQuery-1.9.1源码分析系列(八) 属性操作相关推荐

  1. jQuery源码分析系列:属性操作

    属性操作 1.6.1相对1.5.x最大的改进,莫过于对属性.attr()的重写了.在1.6.1中,将.attr()一分为二: .attr()..prop(),这是一个令人困惑的变更,也是一个破坏性的升 ...

  2. 菜鸟读jQuery 2.0.3 源码分析系列(1)

    原文链接在这里,作为一个菜鸟,我就一边读一边写 jQuery 2.0.3 源码分析系列 前面看着差不多了,看到下面一条(我是真菜鸟),推荐木有入门或者刚刚JS入门摸不着边的看看,大大们手下留情,想一起 ...

  3. jQuery源码分析系列

    声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://git ...

  4. [转]jQuery源码分析系列

    文章转自:jQuery源码分析系列-Aaron 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://github.com/JsAaro ...

  5. [转载]jQuery1.6.1源码分析系列

    转载:http://www.cnblogs.com/nuysoft/archive/2011/11/14/2248023.html [原创] jQuery1.6.1源码分析系列(停止更新) 作者:nu ...

  6. MyBatis 源码分析系列文章合集

    1.简介 我从七月份开始阅读MyBatis源码,并在随后的40天内陆续更新了7篇文章.起初,我只是打算通过博客的形式进行分享.但在写作的过程中,发现要分析的代码太多,以至于文章篇幅特别大.在这7篇文章 ...

  7. MyBatis 源码分析系列文章导读

    1.本文速览 本篇文章是我为接下来的 MyBatis 源码分析系列文章写的一个导读文章.本篇文章从 MyBatis 是什么(what),为什么要使用(why),以及如何使用(how)等三个角度进行了说 ...

  8. Spring IOC 容器源码分析系列文章导读

    1. 简介 前一段时间,我学习了 Spring IOC 容器方面的源码,并写了数篇文章对此进行讲解.在写完 Spring IOC 容器源码分析系列文章中的最后一篇后,没敢懈怠,趁热打铁,花了3天时间阅 ...

  9. Spring IOC 容器源码分析系列文章导读 1

    1. 简介 Spring 是一个轻量级的企业级应用开发框架,于 2004 年由 Rod Johnson 发布了 1.0 版本.经过十几年的迭代,现在的 Spring 框架已经非常成熟了.Spring ...

  10. dubbo源码分析系列(1)扩展机制的实现

    1 系列目录 dubbo源码分析系列(1)扩展机制的实现 dubbo源码分析系列(2)服务的发布 dubbo源码分析系列(3)服务的引用 dubbo源码分析系列(4)dubbo通信设计 2 SPI扩展 ...

最新文章

  1. 破解数据流通痛点,华控清交的隐私计算之道
  2. (七)使用jedis连接单机和集群(一步一个坑踩出来的辛酸泪)
  3. android 模拟器配置上网_10 款主机模拟器,让你畅玩全球大作,嗨到飞起
  4. OC和JS互相调用小框架
  5. 这几部经典纪录片,竟然还有人没看过?
  6. Bootstrap 4:如何使顶部固定的Navbar保持在容器中而不拉伸?
  7. linux mysql使用
  8. Java Swing 如何关闭当前窗口?
  9. tensorflow精进之路(二十四)——Object Detection API目标检测(中)(COCO数据集训练的模型—ssd_mobilenet_v1_coco模型)
  10. SlickEdit 之缘起
  11. 2020虚拟机下载教程(图文详解)
  12. b类 蚂蚁金服_终于拿到蚂蚁金服Offer!!!分享一下全程面试题和面试经验!...
  13. 2019年肖秀荣命题人精讲精练
  14. 易安卓手机APP教程
  15. PHP解决某些特殊汉字符或汉字转码后成乱码或者空白的问题
  16. java freemarker转PDF和Word
  17. 教你如何学习思维导图
  18. Communication error with Jack server
  19. 普莱菲尔密码矩阵生成算法
  20. Mac如何更改系统默认的播放器?

热门文章

  1. VS2010中添加WebService注意的几个地方
  2. windows 下访问bsd系统分区的小工具 ____FFS driver mount manager
  3. this指向问题(call、apply、blind),自我理解的
  4. 350. Intersection of Two Arrays II
  5. liunx 之 redHat 下 java 环境的配置和安装
  6. 用Sql Server 2000的数据库备份来还原Sql Server 2005中的数据库
  7. linux下批量查找UTF-8的BOM文件,并去除BOM
  8. PHP 5.6 已结束安全支持,你升级到 PHP 7 系列了吗?
  9. 第二章 算法 (大话数据结构)
  10. 除了停电之外,今年的CES还有这些“意外”……