本文的开始,我们先看一个例子:
<!DOCTYPE html>
<html>
<body>
<input type="checkbox" />test
<script src="http://libs.baidu.com/jquery/1.6.1/jquery.min.js"></script>
<script>
$("input").click(function () {
console.log($(this).attr('checked'))
})
</script>
</body>
</html>
我们发现结果是checked
和undefined
。
如果我们把jQuery版本换成1.9.1,结果则都为 undefined
。
在jQuery 1.6 以后的版本中,增加了prop
这个方法。为什么有了attr
方法,还要新增一个类似的方法呢?
因为在 jQuery 1.6 之前,使用attr
方法有时候会出现不一致的行为。所以有了prop
方法。那么我们什么时候使用attr
,什么时候使用prop
呢?
具有 true 和 false 两个属性的属性,如 checked, selected 或者 disabled
使用prop(),其他的使用 attr()。
也有文章中写道:
- 对于HTML元素本身就带有的固有属性,在处理时,使用prop方法。
- 对于HTML元素我们自己自定义的DOM属性,在处理时,使用attr方法。
这两种理解方法都可以。
接下来我们看看为什么不同版本的jQuery中attr
行为有差异。
先看1.6.1版本中的attr方法:
attr: function( elem, name, value, pass ) {
var nType = elem.nodeType;
// don't get/set attributes on text, comment and attribute nodes
if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
return undefined;
}
if ( pass && name in jQuery.attrFn ) {
return jQuery( elem )[ name ]( value );
}
// Fallback to prop when attributes are not supported
if ( !("getAttribute" in elem) ) {
return jQuery.prop( elem, name, value );
}
var ret, hooks,
notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
// Normalize the name if needed
name = notxml && jQuery.attrFix[ name ] || name;
hooks = jQuery.attrHooks[ name ];
if ( !hooks ) {
// Use boolHook for boolean attributes
if ( rboolean.test( name ) &&
(typeof value === "boolean" || value === undefined || value.toLowerCase() === name.toLowerCase()) ) {
hooks = boolHook;
// Use formHook for forms and if the name contains certain characters
} else if ( formHook && (jQuery.nodeName( elem, "form" ) || rinvalidChar.test( name )) ) {
hooks = formHook;
}
}
if ( value !== undefined ) {
if ( value === null ) {
jQuery.removeAttr( elem, name );
return undefined;
} else if ( hooks && "set" in hooks && notxml && (ret = hooks.set( elem, value, name )) !== undefined ) {
return ret;
} else {
elem.setAttribute( name, "" + value );
return value;
}
} else if ( hooks && "get" in hooks && notxml ) {
return hooks.get( elem, name );
} else {
ret = elem.getAttribute( name );
// Non-existent attributes return null, we normalize to undefined
return ret === null ?
undefined :
ret;
}
}
1.9.1中attr
方法:
attr: function( elem, name, value ) {
var hooks, notxml, ret,
nType = elem.nodeType;
// don't get/set attributes on text, comment and attribute nodes
if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
return;
}
// Fallback to prop when attributes are not supported
if ( typeof elem.getAttribute === core_strundefined ) {
return jQuery.prop( elem, name, value );
}
notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
// All attributes are lowercase
// Grab necessary hook if one is defined
if ( notxml ) {
name = name.toLowerCase();
hooks = jQuery.attrHooks[ name ] || ( rboolean.test( name ) ? boolHook : nodeHook );
}
if ( value !== undefined ) {
if ( value === null ) {
jQuery.removeAttr( elem, name );
} else if ( hooks && notxml && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
return ret;
} else {
elem.setAttribute( name, value + "" );
return value;
}
} else if ( hooks && notxml && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {
return ret;
} else {
// In IE9+, Flash objects don't have .getAttribute (#12945)
// Support: IE9+
if ( typeof elem.getAttribute !== core_strundefined ) {
ret = elem.getAttribute( name );
}
// Non-existent attributes return null, we normalize to undefined
return ret == null ?
undefined :
ret;
}
}
我们在看看其中都有用到的boolHook
:
1.6.1中:
// Hook for boolean attributes
boolHook = {
get: function( elem, name ) {
// Align boolean attributes with corresponding properties
return elem[ jQuery.propFix[ name ] || name ] ?
name.toLowerCase() :
undefined;
}
};
1.9.1中:
// Hook for boolean attributes
boolHook = {
get: function( elem, name ) {
var
// Use .prop to determine if this attribute is understood as boolean
prop = jQuery.prop( elem, name ),
// Fetch it accordingly
attr = typeof prop === "boolean" && elem.getAttribute( name ),
detail = typeof prop === "boolean" ?
getSetInput && getSetAttribute ?
attr != null :
// oldIE fabricates an empty string for missing boolean attributes
// and conflates checked/selected into attroperties
ruseDefault.test( name ) ?
elem[ jQuery.camelCase( "default-" + name ) ] :
!!attr :
// fetch an attribute node for properties not recognized as boolean
elem.getAttributeNode( name );
return detail && detail.value !== false ?
name.toLowerCase() :
undefined;
}
}
我们可以发现,在不同版本jQuery中boolHook的set方法处理不同,在1.6.1版本中相当于:
// 能得到正确的布尔值
document.getElementsByTagName("input")[0]['checked']
而在1.9.1中相当于:
// 结果为null
document.getElementsByTagName("input")[0].getAttribute("checked")
那prop
和attr
的区别主要在哪呢,我们看看prop
的实现(1.6.1和1.9.1 版本prop实现方法无本质区别,这里只列举1.9.1版本):
prop: function( elem, name, value ) {
var ret, hooks, notxml,
nType = elem.nodeType;
// don't get/set properties on text, comment and attribute nodes
if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
return;
}
notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
if ( notxml ) {
// Fix name and attach hooks
name = jQuery.propFix[ name ] || name;
hooks = jQuery.propHooks[ name ];
}
if ( value !== undefined ) {
if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
return ret;
} else {
return ( elem[ name ] = value );
}
} else {
if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {
return ret;
} else {
return elem[ name ];
}
}
}
在attr
方法中核心代码为:
elem.setAttribute( name, "" + value );
ret = elem.getAttribute( name );
在prop
方法中核心代码为:
elem[ name ] = value;
elem[ name ];
这样差异就一目了然了。
所以我们平时用到这两个方法时需要区分下两者的区别。