前幾天,有人給 Multiple Select 插件 提了問題:javascript
setSelects doesn't work in Firefox when using jquery 1.9.0php
一直都在用 jQuery 1.8.3 的版本,沒有嘗試過 jQuery 1.9.0 的版本。java
因而,開始調試代碼,在 1.9.0 的版本中:node
<input type="checkbox" /> <script> $(function() { $('input').click(function() { $(this).attr('checked'); }); }); </script>
點擊 checkbox,結果都是 undefinedjquery
而在 1.8.3 的版本中,結果是 checked 和 undefinedgit
到這裏,問題答案找到了,就是使用 attr() 方法的問題,因而查看官方文檔, 才知道從 jQuery 1.6 開始新增了一個方法 prop(),可是一直都沒有使用過。github
從中文意思看,二者分別是獲取/設置 attributes 和 properties 的方法,那麼爲何還要增長 prop() 方法呢?web
Before jQuery 1.6, the .attr() method sometimes took property values into account when retrieving some attributes, which could cause inconsistent behavior.
由於在 jQuery 1.6 以前,使用 attr() 有時候會出現不一致的行爲。fetch
那麼,何時使用attr(),何時使用prop()?this
To retrieve and change DOM properties such as the checked, selected, or disabled state of form elements, use the .prop() method.
根據官方的建議:具備 true 和 false 兩個屬性的屬性,如 checked, selected 或者 disabled 使用prop(),其餘的使用 attr()
到此,將 attr('checked') 改爲 prop('checked') 便可修復提的 issues 了。
^_^
等等,貌似問題還沒真正解決,爲何開頭例子中 jQuery 1.8.3 和 1.9.0 使用 attr() 會有所區別呢?
想知道他們的區別,最好的辦法仍是看他們的源代碼:
1.8.3 attr():
attr: function( elem, name, value, pass ) { var ret, hooks, notxml, nType = elem.nodeType; // don't get/set attributes on text, comment and attribute nodes if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { return; } if ( pass && jQuery.isFunction( jQuery.fn[ name ] ) ) { return jQuery( elem )[ name ]( value ); } // Fallback to prop when attributes are not supported if ( typeof elem.getAttribute === "undefined" ) { 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 ); return; } 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 && (ret = hooks.get( elem, name )) !== null ) { return ret; } else { ret = elem.getAttribute( name ); // Non-existent attributes return null, we normalize to undefined return ret === null ? undefined : ret; } }
1.9.0 attr():
attr: function( elem, name, value ) { var ret, hooks, notxml, 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 === "undefined" ) { 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 !== "undefined" ) { ret = elem.getAttribute( name ); } // Non-existent attributes return null, we normalize to undefined return ret == null ? undefined : ret; } }
1.8.3 和 1.9.0 的 prop() 是同樣的:
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() 和 prop() 的區別:
attr() 裏面,最關鍵的兩行代碼
elem.setAttribute( name, value + "" ); ret = elem.getAttribute( name );
很明顯的看出來,使用的 DOM 的 API setAttribute() 和 getAttribute() 方法操做的屬性元素節點。
prop() 裏面,最關鍵的兩行代碼
return ( elem[ name ] = value ); return elem[ name ];
能夠理解爲 document.getElementById(el)[name] = value,這是轉化成 element 的一個屬性。
對比調試 1.9.0 和 1.8.3 的 attr() 方法,發現二者的區別在於
hooks.get( elem, name ))
返回的值不同,具體的實現:
1.8.3 中
boolHook = { get: function( elem, name ) { // Align boolean attributes with corresponding properties // Fall back to attribute presence where some booleans are not supported var attrNode, property = jQuery.prop( elem, name ); return property === true || typeof property !== "boolean" && ( attrNode = elem.getAttributeNode(name) ) && attrNode.nodeValue !== false ? name.toLowerCase() : undefined; } }
1.9.0 中
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; } }
因而可知,1.9.0 開始不建議使用 attr() 來對具備 true 和 false 兩個屬性的屬性進行操做了。
那麼咱們的結論是:
具備 true 和 false 兩個屬性的屬性,如 checked, selected 或者 disabled 使用prop(),其餘的使用 attr(),具體見下表:
注:本文中的大部分觀點以及例子屬於我的理解,不免還有不許確的地方,歡迎有相關研究的同行指正。