2014年7月21日 星期一

jQuery 中 attr() 與 prop() 使用區別

相信有在使用 jQuery 一定不陌生這 2 個函式,

但是或許並不清楚這 2 種使用上的區別.

總之還是先看官方上的介紹與源代碼吧 !



jQuery 1.6 前都是大多都是使用 attr() 去改變元素屬性,
紅框是在提示檢索和改變 DOM 屬性最好是用 .prop() 方法
相較 .attr(), .prop() 是 jQuery 1.6 之後釋出的新功能,

從字意上來看都是 獲取/設置 屬性的方法,

但是看過實作下面這些測試就會清楚兩者之間的差別了.

我們先瀏覽 .attr() 與 .prop() 源代碼.

.attr() - jQuery 1.8.3

 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;  
   }  
 }  

.prop() - jQuery 1.8.3

 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() 中發現了幾行代碼如下 :

  1. typeof elem.getAttribute === "undefined"
  2. elem.setAttribute( name, value + "" );
  3. ret = elem.getAttribute( name );

發現了都是使用 DOM 的 API setAttribute 與 getAttribute 去操作 elem 屬性元素節點.

而 .prop() 中則是使用了以下代碼 :

  1. return ( elem[ name ] = value );
  2. return elem[ name ];

則是把 elem 當作了物件並直接操作物件的屬性.

我們直接拿 checkbox 來測試看看或許就更清楚明白了.

範例 1: 預設不打勾 checkbox, 使用 attr() 獲取 checked 為 undefined
簡單來說 .attr() 就是透過 getAttribute() 來獲取對象屬性節點的值,

可是例子中的 <input /> 一開始預設沒有 checked 屬性當然也就是輸出 undefined.

再來 .prop() 獲取的是對於一個 DOM 對象具有原生的 checked 屬性當然就是輸出 false.

這邊分享一個文章來建立 HTML attribute 與 property 不同的觀念.


當然原理已經從上面這些例子得證了,

也不難理解為什麼官方提示在獲取 / 設置

checked, selected, readonly 和 disabled 等屬性時使用 .prop() 方法比 .attr() 方法更好.

其中一個原因就是布林值比字串值的處理更合適.

再來就是對於效能來比較, .prop() > .attr(), 因為訪問 DOM 屬性節點是很耗時的.

額外參閱 : 前端開發

沒有留言: