jQuery attr() vs. prop()
簡介
在 jQuery 中有兩個函式 attr() 和 prop() 用以取得選取元件的 attribute 和 property 的屬性值。在中文裡 attribute 和 property 都譯成「屬性」,而 attr() 和 prop() 兩個函式的功能和用法似乎也一樣,那為什麼要分成兩個函式呢?原來,在 jQuery v1.6 之前,這兩個函式確實沒有什麼分別,幾乎是共通的;但在 jQuery v1.6 後, jQuery 的開發人員針對 attribute 和 property 作了更精準的定義,為的是讓這兩個函式有明確的功能區分。
新的定義大略是這樣的:attribute 代表的是 DOM 元件中所定義的屬性,而 property 是 DOM tree 中元件之的屬性當時的狀態。這是我能想到的最佳的解釋了。我的理解是這樣的:HTML 文件實質上是一份文檔,在流覽器將這份文檔讀入後,在記憶體中建立了 DOM tree,以節點的方式記錄了 HTML 文件中對各元件所設定的屬性 (attribute) 及其屬性值。jQuery 為了運作方便,又將元件以 jQuery Object 包裝起來,用 property 屬性來表示這些 attribute 屬性目前的狀態。按理說,修改了其中的一個值,應該也會修改另一個值,也就是兩個值理當是同步的。實際上並非如此,像是 text 或 number input 欄位中的 value 屬性,代表的是 input 欄位的預設值,只要不曾用 attr() 函式去改變其設定值,每次 attr() 都會取得原先在 HTML 文件中設定的值。而 property 中也有個 value 屬性,代表的卻是使用者目前在欄位中的輸入值,一開始兩者的值是一樣的,但只要使用者在欄位中有輸入任何值,便會改變 property 中 value 屬性的值。此兩者的定義不同,名稱雖然一樣,屬性值卻不會同步,也的確不該同步。然而其它的的屬性,例如: autofocus, checked 這類屬性,在 HTML 中有兩種寫法:<input type="text" autofocus> 或 <input type="text" autofocus="autofocus">,這兩種寫法都可以,也都能得到正確的結果,以 attr() 取得其屬性值時,傳回值為 "autofocus" 的字串或是 undefined ;而使用 prop() 取得的值卻是 true 和 false 的布林值。checked 屬性也是一樣的情況。這一類的屬性就造成了一些混亂。在範例程式碼中做了一些測試,有個情況看其來確實不合理,不知道算不算 jQuery 的 bug。
結論是,attr() 的傳回值不是字串,就是 undefined。prop() 的傳回值卻會因屬性的不同而有各種可能。在使使用 jQuery 開發時,盡可能使用 prop() 去操作屬性的值,除了像 input 中的 value 欄位那一類型的屬性,和 property 的屬性同名,卻有不同的定義,要在程式中改變欄位的預設值,不得不用 attr() 函式外,其它的都用 prop(),就安全多了。
範例
範例中建立了一個文字輸入欄位及兩個檢核框 (checkbox) 用來測試 value, autofocus 及 checked 等屬性。網頁下載完畢時,在輸入欄位中隨便輸入一個值,按下 submit 按鈕後,打開「開發者工具」的「主控台」查看輸入的結果。
CSS
<style> html { width:100%; height:auto; font:normal normal normal 16px Arial, '文泉驛正黑', 'WenQuanYi Zen Hei', '儷黑 Pro', 'LiHei Pro', '微軟正黑體', 'Microsoft JhengHei'; line-height:1.7; letter-spacing: 0.087em; } body { margin:0 auto; width:1000px; height:100%; padding-top:20px; } input { font-size: 1em; } </style>
CSS 中沒什麼重要的樣式設定,一般流覽器中對 input 欄位的字型預設的都比較小,所以 input{} 中將字型大小設為 1em,也就是 16px,比較容易看清楚。
HTML
<body>
</body>
HTML 中建立一個文字輸入欄位,預設值為 "someone",這個值在網頁出現時,就會放在文字輸入框中做為預設值,並設定 autofocus,該該輸入欄位自動取得焦點。另建立兩個檢核框,以測試 checked 屬性,兩個檢核框各以不同的設定方法設定檢核框為核取狀態。
Script
<script> $(function(){ $("#submit").button().click(function(){ var input = $("#input"); console.log('----- input text field -----'); console.log('$("#input").attr("nodeType") = %o', input.attr("nodeType")); // undefined console.log('$("#input").prop("nodeType") = %o', input.prop("nodeType")); // 1 console.log('$("#input").attr("value") = %o', input.attr("value")); // "someone" console.log('$("#input").prop("value") = %o', input.prop("value")); // "john" console.log('$("#input").attr("autofocus") = %o', input.attr("autofocus")); // "autofocus" console.log('$("#input").prop("autofocus") = %o', input.prop("autofocus")); // true var cb1 = $("#cb1"); var cb2 = $("#cb2"); console.log('----- checkbox -----'); console.log('$("#cb1").attr("checked") = %o', cb1.attr("checked")); // "checked" console.log('$("#cb1").prop("checked") = %o', cb1.prop("checked")); // true console.log('$("#cb2").attr("checked") = %o', cb2.attr("checked")); // "checked" console.log('$("#cb2").prop("checked") = %o', cb2.prop("checked")); // true $("#cb1").prop("checked", false); console.log('----- after $("#cb1").prop("checked", false);" -----'); console.log('$("#cb1").attr("checked") = %o', cb1.attr("checked")); // "checked" console.log('$("#cb1").prop("checked") = %o', cb1.prop("checked")); // false $("#cb2").attr("checked", false); console.log('----- after $("#cb2").attr("checked", false);" -----'); console.log('$("#cb2").attr("checked") = %o', cb2.attr("checked")); // undefined console.log('$("#cb2").prop("checked") = %o', cb2.prop("checked")); // false $("#cb1").prop("checked", true); console.log('----- after $("#cb1").prop("checked", true);" -----'); console.log('$("#cb1").attr("checked") = %o', cb1.attr("checked")); // "checked" console.log('$("#cb1").prop("checked") = %o', cb1.prop("checked")); // true $("#cb2").attr("checked", true); console.log('----- after $("#cb2").attr("checked", true);" -----'); console.log('$("#cb2").attr("checked") = %o', cb2.attr("checked")); // "checked" console.log('$("#cb2").prop("checked") = %o', cb2.prop("checked")); // false }); }); </script>
注意第 6 行和第 7 行的值,nodeType 這個屬性,在 attribute 中沒有定義,在 property 中的值為 1,代表 element node。
當 checked 屬性有定義時,attr("checked") 的傳回值為 "checked" 字串, prop("checked") 傳回值為 true。當沒有定義 checked 值時,attr("checked") 的傳回值為 undefined,prop("checked") 傳回值為 false。一切看起來都很正常,直到第 35 行,用 attr("checked", true); 去設定 checked 時,第 39 行 prop("checked") 的傳回值卻是 false,螢幕上的檢核框也沒有顯示核取的狀態,表示 attribute 和 property 屬性的值在此時並沒有同步。我想這應該算是個 bug,解決方案就是用 prop() 函式來處理。目前用的是 jQuery 2.2.2 版,不知下一版會不會修改。
沒有留言:
張貼留言