jQuery-1.9.1源码分析系列(八) 属性操作
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对象的每一个元素设置val
return 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;
}
});
}
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();
//查找hook
hooks = 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<8
if ( !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<8
if ( !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 );
}
}
}
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 hooks
name = jQuery.propFix[ name ] || name;
//查找hook
hooks = 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 names
var className,
i = 0,
self = jQuery( this ),
state = stateVal,
classNames = value.match( core_rnotwhite ) || []; while ( (className = classNames[ i++ ]) ) {
// check each className given, space separated list
state = 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 set
jQuery._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__" ) || "";
}
});
},
如果觉得本文不错,请点击右下方【推荐】!
jQuery-1.9.1源码分析系列(八) 属性操作的更多相关文章
- jQuery 2.0.3 源码分析 钩子机制 - 属性操作
jQuery提供了一些快捷函数来对dom对象的属性进行存取操作. 这一部分还是比较简单的. 根据API这章主要是分解5个方法 .attr() 获取匹配的元素集合中的第一个元素的属性的值 或 设置 ...
- jQuery源码分析系列(38) : 队列操作
Queue队列,如同data数据缓存与Deferred异步模型一样,都是jQuery库的内部实现的基础设施 Queue队列是animate动画依赖的基础设施,整个jQuery中队列仅供给动画使用 Qu ...
- jQuery源码分析系列
声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://git ...
- [转]jQuery源码分析系列
文章转自:jQuery源码分析系列-Aaron 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://github.com/JsAaro ...
- jQuery源码分析系列(转载来源Aaron.)
声明:非本文原创文章,转载来源原文链接Aaron. 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://github.com/JsAa ...
- jQuery源码分析系列——来自Aaron
jQuery源码分析系列——来自Aaron 转载地址:http://www.cnblogs.com/aaronjs/p/3279314.html 版本截止到2013.8.24 jQuery官方发布最新 ...
- jquery2源码分析系列
学习jquery的源码对于提高前端的能力很有帮助,下面的系列是我在网上看到的对jquery2的源码的分析.等有时间了好好研究下.我们知道jquery2开始就不支持IE6-8了,从jquery2的源码中 ...
- jQuery-1.9.1源码分析系列完毕目录整理
jQuery 1.9.1源码分析已经完毕.目录如下 jQuery-1.9.1源码分析系列(一)整体架构 jQuery-1.9.1源码分析系列(一)整体架构续 jQuery-1.9.1源码分析系列(二) ...
- MyCat源码分析系列之——结果合并
更多MyCat源码分析,请戳MyCat源码分析系列 结果合并 在SQL下发流程和前后端验证流程中介绍过,通过用户验证的后端连接绑定的NIOHandler是MySQLConnectionHandler实 ...
- MyCat源码分析系列之——SQL下发
更多MyCat源码分析,请戳MyCat源码分析系列 SQL下发 SQL下发指的是MyCat将解析并改造完成的SQL语句依次发送至相应的MySQL节点(datanode)的过程,该执行过程由NonBlo ...
随机推荐
- PHP基础知识之魔术方法
__construct(), __destruct(), __call(), __callStatic(), __get(), __set(), __isset(), __unset(), __sle ...
- 简单明了区分escape、encodeURI和encodeURIComponent
一.前言 讲这3个方法区别的文章太多了,但是大部分写的都很绕.本文试图从实践角度去讲这3个方法. 二.escape和它们不是同一类 简单来说,escape是对字符串(string)进行编码(而另外两种 ...
- 【腾讯Bugly干货分享】Android ImageView 正确使用姿势
本文来自于腾讯bugly开发者社区,未经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/5832602d7196970d65901d76 导语 本文主要介绍了ImageV ...
- Windows建立Cucumber和Ruby测试环境
1. 下载安装Ruby1.9.3, 不要用RubyInstall 一键安装,下载zip然后解压到c:\Ruby193 (不要用2.0,用2.0安装不成功,不要怪我) 2. 环境变量配置RUBY_HOM ...
- Python黑帽编程 3.3 MAC洪水攻击
Python灰帽编程 3.3 MAC洪水 传统的交换机(我只对我目前使用的交互机做过测试,按照常识只能这样表述)在数据转发过程中依靠对CAM表的查询来确定正确的转发接口,一旦在查询过程中无法找到相关目 ...
- 递归算法经典实例小结(C#实现)
一 .递归算法简介 在数学与计算机科学中,递归是指在函数的定义中使用函数自身的方法. 递归算法是一种直接或者间接地调用自身算法的过程.在计算机编写程序中,递归算法对解决一大类问题是十分有效的,它往往 ...
- .NET实现微博粉丝服务平台接口
[文章摘要]Senparc.Weixin.MP虽然是微信公众号的SDK,但由于易信公众号和新浪微博粉丝服务平台也提供了微信兼容接口,所以也可以使用其快速实现相应的服务,当然微博由于与微信存在差异,如果 ...
- awk 留底
序 因为经常做awk编码,而且跨过一段时间就容易忘记,故在此做个留底.便于翻阅.——后期会在这个页面不断补充! 常用常量 属性 描述 NR 已读入的总记录数 ARGIND 当前被 ...
- ystep jQuery流程、步骤插件
今天小菜给大家带来又一款给力jQuery插件:ystep. 从名称上大致可以看出,这是一个流程步骤插件. 如果无意外的话,这可能是小菜近期最后一个作品了...苦逼的小菜即将创业,可能就没时间折腾啦~好 ...
- CSharpGL(0)一个易学易用的C#版OpenGL
+BIT祝威+悄悄在此留下版了个权的信说: CSharpGL(0)一个易学易用的C#版OpenGL CSharpGL是我受到SharpGL的启发,在整理了SharpGL,GLM,SharpFont等开 ...