首页 > 代码库 > jquery源码解析:jQuery对元素属性的操作1
jquery源码解析:jQuery对元素属性的操作1
我们先来看一下jQuery中有多少个方法是用来操作元素属性的。
首先,看一下实例方法:
然后,看下静态方法(工具方法):
静态方法是内部使用的,我们外面使用的很少,实例方法才是对外的。
接下来,我们来看下一些方法是如何使用的?
$("#div1").attr("title","hello") ,设置属性,两个参数时。
$("#div1").attr("title") , 获取属性值,一个参数时。
$("#div1").prop("title"),也可以获得这个属性值。
它们之间的区别就是:attr相当于原生的setAttribute(),getAttribute()。
prop相当于原生的.属性名([属性名]),比如:div.title = "hello";div.title。
在这里简单的讲一下原生的区别:
$("#div1").attr("chaojidan","hello") ,给元素添加属性名为chaojidan的属性。在元素div标签上会显示chaojidan这个属性。
$("#div1").prop("chaojidan","hello"),也是给元素添加属性名为chaojidan的属性,但是在元素div标签上不会显示这个属性。
因为chaojidan是自定义属性,不是元素的固有属性。
还有一个区别:
<div chaojidan="hello" id="div1">
$("#div1").attr("chaojidan") 返回hello。但是$("#div1").prop("chaojidan"),在有些浏览器下会返回空。因为chaojidan是自定属性。
还有一个区别:
对于a标签的href属性,attr返回href的属性值,但是prop返回document.URL + href的属性值。href是a标签的固有属性。
更具体的区别,请看:http://www.cnblogs.com/chaojidan/p/4108777.html
固有属性,如果你用removeProp删除的话,会有问题,比如,删除一个元素的id,是删除不掉的。但是用removeAttr就可以。因此prp和removeProp用的比较少,而attr和removeAttr用的比较多。
最后,我们来看源码:
jQuery.fn.extend({
attr: function( name, value ) {
return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 ); //此方法之前讲过,如果arguments.length > 1,就代表是设置操作,如果是false,那就代表是获取操作。而真正调用的回调方法是静态方法:jQuery.attr。name就是你传进来的属性名,value是你传进来的属性值。
},
removeAttr: function( name ) {
return this.each(function() {
jQuery.removeAttr( this, name ); //实例方法removeAttr,调用的也是同名的静态方法removeAttr。
});
},
prop: function( name, value ) { //也是静态方法jQuery.prop
return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 );
},
removeProp: function( name ) {
return this.each(function() {
delete this[ jQuery.propFix[ name ] || name ]; //删除属性,如果属性名需要做兼容,就做兼容,比如:class要变成className。
});
},
......
});
jQuery.extend({
attr: function( elem, name, value ) {
var hooks, ret,
nType = elem.nodeType;
if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { //元素不存在或文本节点,或注释节点,或属性节点,不能设置属性,直接返回。元素节点才能设置属性。
return;
}
if ( typeof elem.getAttribute === core_strundefined ) { //core_strundefined = "undefined"。document,window没有getAttribute方法,因此使用prop方法,而prop方法是用.属性名的形式。
return jQuery.prop( elem, name, value );
}
if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { //如果不是元素节点或元素节点是不是xml文档下的,如果是,那么jQuery.isXMLDoc( elem ) 返回true。这里的意思就是:如果是xml文档下的元素节点,就不会进入到if语句。xml文档下的元素都是自定义的,没有兼容性问题。所以不需要进入到if语句,进行兼容性处理。而html文档下的元素节点,有兼容性问题,所以需要做下处理。
name = name.toLowerCase();
//hooks是jQuery中专门用来解决兼容性问题的。support用来检测浏览器的兼容性,hooks来解决兼容性问题,hooks针对不同的类型有相对应的hooks,比如:attr,就对应于attrHooks。hooks分两种,一种是针对设置的兼容性处理,set方法,一种是针对获取的兼容性处理,get方法。如果有兼容性问题,set方法或get方法会返回兼容性处理之后的值,如果没有兼容性问题,set就会返回undefined,get就会返回null。大家可以看下attrHooks对象,其实传属性名进来,只有type属性才有兼容性问题。而且只针对设置操作,获取操作没有兼容性问题。具体一点就是:设置type = "radio" 的兼容性问题。
hooks = jQuery.attrHooks[ name ] || ( jQuery.expr.match.bool.test( name ) ? boolHook : nodeHook ); //jQuery.expr = Sizzle.selectors,Sizzle.selectors对象中有match: matchExpr属性。matchExpr也是一个对象,它里面的bool属性值是:new RegExp( "^(?:" + booleans + ")$", "i" )。而booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped"。nodeHook = undefined。如果不是在IE下设置type类型为radio,那么就会判断name是否匹配此正则。如果匹配,就返回boolHook,而不匹配就会返回undefined。boolHook是用来专门处理bool类型属性的。比如:<input type="checkbox" checked="checked">, $("input").attr("checked") : checked,$("input").prop("checked") : true。checked属性就属于bool类型属性。针对以上这个例子,我们知道attr获取checked的值是checked,因为当我们设置是也应该$("input").attr("checked","checked"),但有些人可能对jQuery不熟,会写成$("input").attr("checked",true),那么这种写法行不行呢,也是可以的,因为jQueyr里面做了兼容处理。其实就是boolHook对象,大家可以在文章的最后看到这个对象,看它是如何处理的。
}
if ( value !== undefined ) { //设置操作
if ( value =http://www.mamicode.com/== null ) { //$("#div1").attr("chaojidan",null)这种情况,会把chaojidan的属性移除。
jQuery.removeAttr( elem, name );
} else if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
return ret; //如果有兼容性问题,就进行处理,然后把处理的值返回。
} else {
elem.setAttribute( name, value + "" ); //用普通的方式,进行设置操作。因为属性值都是字符串,所以把number转化成字符串。
return value;
}
} else if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {
return ret; //获取操作。先看是否有get的兼容性操作。
} else {
ret = jQuery.find.attr( elem, name ); //jQuery.find = Sizzle。Sizzle中的attr方法对getAttribute 方法进行了兼容性处理。
return ret == null ? undefined : ret;
}
},
attrHooks: {
type: {
set: function( elem, value ) { //这个方法是解决这样一个问题的:input = document.createElement("input");input.value = "http://www.mamicode.com/t";input.type = "radio";support.radioValue = http://www.mamicode.com/input.value ==="t";当你先对input的value赋值,然后再设置input的type为radio时,IE下的input的value会变成on,而其他浏览器会得到t。
if ( !jQuery.support.radioValue && value =http://www.mamicode.com/=="radio" && jQuery.nodeName(elem, "input") ) { //如果存在以上这个兼容性问题,也就是jQuery.support.radioValue =http://www.mamicode.com/false,IE下是false,value就是你设置type属性的值,并且元素是input,意思就是:你对input元素设置type=radio的操作。
var val = elem.value; //怎么解决IE下的这个兼容性问题呢,我们先把这个input的value值保存起来。等设置了type = "radio" 后,再把值赋过去。这样它的input的value就不会变成on了。
elem.setAttribute( "type", value );
if ( val ) {
elem.value = http://www.mamicode.com/val;
}
return value;
}
}
}
},
removeAttr: function( elem, value ) {
var name, propName,
i = 0,
attrNames = value && value.match( core_rnotwhite ); //core_rnotwhite = /\S+/g,这里的意思就是可以同时删除多个属性值,比如:$("div").removeAttr("id name class");,value = "http://www.mamicode.com/id name class",调用match方法,并传入正则/\S+/g,会返回[id,name,class]。
if ( attrNames && elem.nodeType === 1 ) { //必须是元素节点Element
while ( (name = attrNames[i++]) ) {
propName = jQuery.propFix[ name ] || name; //propFix: {"for": "htmlFor","class":"className"},如果要删除的属性名是for或者class,那么需要做兼容处理。因此你做$("div").removeAttr("class")操作时,就不会出问题。
if ( jQuery.expr.match.bool.test( name ) ) { //如果要删除的属性名属于bool型的属性(也就是说它的值通过[属性名]获取时,是false或者true)
elem[ propName ] = false; //需要把此bool型的属性值赋为false,因为这个属性已经被移除了,不应该用[属性名]获取时,返回true。比如:input元素的checked属性,当你移除这个checked属性时,你通过input.checked获得true,那么就会被认为input中有这个checked,而这时checked你已经移除了,所以必须设置它的input.checked=false。具体请看这里:http://www.cnblogs.com/chaojidan/p/4108777.html
}
elem.removeAttribute( name ); //调用原生的方法移除掉
}
}
},
prop: function( elem, name, value ) {
var ret, hooks, notxml,
nType = elem.nodeType;
if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
return;
}
notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
if ( notxml ) { //不是xml文档,需要做兼容处理
name = jQuery.propFix[ name ] || name; //propFix上面已经讲了
hooks = jQuery.propHooks[ name ]; //如果name是tabIndex,需要做兼容处理。tabIndex可以切换光标的顺序(通过tab键),按元素中tabIndex的属性值大小(1,2,3....),从小到大进行切换。
}
if ( value !== undefined ) { //设置操作
return hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ? ret : ( elem[ name ] = value ); //这里如果返回的是elem[ name ] = value,其实是return value。
} else { //获取操作
return hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ? ret : elem[ name ];
}
},
propHooks: {
tabIndex: { //此属性是在获取时,会有兼容性问题。其实就是在元素默认不支持tabIndex属性时,并且没有显式设置它的tabIndex属性时,IE6-8会返回0,而标准浏览器下会返回-1。所以兼容处理,都返回-1.
get: function( elem ) {
return elem.hasAttribute( "tabindex" ) || rfocusable.test( elem.nodeName ) || elem.href ? elem.tabIndex : -1; //rfocusable = /^(?:input|select|textarea|button)$/i;,如果元素不属于正则中指定的这些元素时,并且元素没有href属性,那么就证明此元素默认不支持tabIndex属性。
}
}
}
......
});
boolHook = {
set: function( elem, value, name ) { //根据例子,假设你设置$("input").attr("checked",true)
if ( value =http://www.mamicode.com/== false ) { //value等于true。如果是$("input").attr("checked",false)就移除checked的属性。
jQuery.removeAttr( elem, name );
} else {
elem.setAttribute( name, name ); //$("input").attr("checked","checked")
}
return name;
}
};
if ( !jQuery.support.optSelected ) { //这里的hooks是针对select元素的第一个option元素是否会默认被选中。
jQuery.propHooks.selected = { //在IE下(老版本safari),不会默认选中,因此获取option的selected值时返回false,而其他浏览器返回true。
get: function( elem ) { //只有get方法,因为只有获取时才会出现这个问题。假设你要获取option的selected属性值。
var parent = elem.parentNode;
if ( parent && parent.parentNode ) {
parent.parentNode.selectedIndex; //只要在获取option的selected的值时,先访问select.selectedIndex属性,就可以设置option.selected = true了。意思就是在访问option的selected属性时,先访问其父级select元素的selectedIndex属性,强迫浏览器计算option的selected属性,以得到正确的值。需要注意的是option元素的父元素不一定是select,也有可能是optgroup。这里是支持IE9+,所以option的parentNode是optgroup,optgroup的parentNode是select。
}
return null;
}
};
}
jQuery.each([ //不懂each方法的,可以去这里看下http://www.cnblogs.com/chaojidan/p/4156600.html
"tabIndex",
"readOnly",
"maxLength",
"cellSpacing",
"cellPadding",
"rowSpan",
"colSpan",
"useMap",
"frameBorder",
"contentEditable"
], function() {
jQuery.propFix[ this.toLowerCase() ] = this; //这里的this就是数组中的选项,比如:jQuery.propFix[ tabIndex.toLowerCase() ] = tabIndex;之所以这样做,是以防有人做jQuery属性操作时,把名字写成了全部是小写的情况,这里做下兼容,使用户输入全部是小写属性名,也能正常操作。
});
这一课,还是比较重要的,牵涉的东西很多,想彻底弄明白,还是需要一定的基础的。
加油!
jquery源码解析:jQuery对元素属性的操作1