屬性與特性



在進入瀏覽器作為客戶端之後,屬性(Attribute)與特性(Property)這兩個名詞就不斷交相出現,到目前還沒正式解釋它們的意義。

其實在正式進入瀏覽器作為客戶端前,對於JavaScript物件本身帶有的名稱,這邊的文件都用特 性這個名詞。例如:
var obj = {
    x : 10,
    y : 20
};

以上文件都稱,物件obj 擁有特性x與y,特性x的值為10,特性y的值為20。

HTML本身可以擁有屬性。例如:
<input name="user" value="guest">

文件中會 稱,<input>標籤擁有屬 性name 與value,屬性值各為user與guest。

瀏覽器會剖析
HTML, 為每個標籤建立對應的DOM物件,完成剖析後,對於HTML的所有屬性(無 論標籤上是否有撰寫),DOM物件上會建立對應的特性,通常屬性名稱是什 麼,特性名稱也會是什麼,如果標籤上有設定某個屬性,則屬性值為何,特性值也就為何,如果 標籤上沒有設置屬性,則DOM物件上的特性會有預設值

例如上例中,<input> 對應的DOM元 素上,name特性與value特性值分別是user與guest。你可以如下分別取得(假設是頁面中第一個<input>標 籤):
var input = document.getElementsByName('user')[0];
var name = input.name;
var value = input.value;

像這時,DOM元素上的name、 value特性,也可以稱之為name與value屬性。也就是說,屬性這個詞,可用來表示HTML中的屬性,也可用來表示DOM 中相對應的特性。對於HTML中沒有設定的標籤屬性,DOM上也會有對應的特性(屬性),不過都是預設值,例如,上 面的<input>標籤並沒有設置type屬性,但DOM物件上對應的特性(屬性),其值為"text"。

不過,HTML的屬性名稱未必與DOM物件的特性(屬性)名稱相對應例如class就是一個例子,因為 class在JavaScript中是關鍵字,在DOM上要取得HTML的class 屬性對應名稱必須使用className,<label> 的for屬性,因為for是關鍵字,而必須使用htmlFor特 性來取得。例如:
<img id="logo" src="images/caterpillar.jpg"
     class="logo" title="Caterpillar's Logo"/>


若要以JavaScript取得HTML的class屬性值,則必須:
var className = document.getElementById('logo').className;

透過JavaScript特性存取方式取得HTML屬性的對應值,也未必是 HTML屬性中真正設定的值。例如,透過JavaScript取得<img> 的src,結果是絕對URL,即使屬性中設定的是相對 URL。

瀏覽器在剖析完HTML後,對於HTML中有設置的屬性,其實會在DOM物件上 建立attributes特性。你可以如下顯示attributes的 元素值,每個元素的型態是Attr
var attributes = document.getElementById('logo').attributes;
for(var i = 0; i < attributes.length; i++) {
    var attrName = attributes[i].nodeName;
    var attrValue = attributes[i].nodeValue;
    ...
}

以物件結構來表示的話:
{
    attributes : {
        '0' : {nodeName : 'id', nodeValue : 'logo', ...},
        '1' : {nodeName : 'src', nodeValue : 'images/src', ...},
        '2' : {nodeName : 'class', nodeValue : 'logo', ...},
        '3' : {nodeName : 'title', nodeValue : 'Caterpillar\’s logo', ...},
        length : 4
        ...
    },
    id : 'logo',
    src : 'https://openhome.cc/images/caterpillar.jpg',
    className : 'logo',
    title : 'Caterpillar\'s logo',
    …
}

Attr
實例上的特性值,是HTML上真正設定的屬性與值。在 文件剖析完畢後,DOM物件上的屬性(特性)與attributesAttr實 例之特性是對應的

注意,上面是以物件結構來示意,並不是指真正的型態就是上面所表示的。attributes 的型態會是 NamedNodeMap ,而每個索引元素的型態會是 Attr(如果你手邊有個JavaScript Debugger的 話,可以很方便地觀察這些東西)。

你可以使用DOM物件的getAttribute() 來取得attributes 中的屬性,使用setAttribute()設定attributes中 的屬性(同時亦會改變DOM對應的特性),使用removeAttribute()來移除attributes屬 性。

移除屬性是指移除attributes上對應的特性值,而非移除DOM物件上對應的特性(屬性)值,DOM物件上對應的特性 (屬性)值在使用removeAttribute()後,只是回到預設值,而不是直接將特性移除,沒 有任何操作可以將DOM上對應屬性的特性 移除。如 果HTML上 沒有設置該屬性,則使用getAttribute()指定該屬性會取得null,但並不表示DOM上沒有對應屬性的特性,而是該特性值會是預設 值。使用setAttribute()可以在attributes中設定屬性,相對應的DOM特性值也會改變。

例如,以下的程式,只會將attributes 中對應src屬性的Attr實例移除,不會移除DOM上src特 性(屬性),DOM上src只 是回到''的 預設值,也就是空字串。
var img = document.getElementById('logo');
img.removeAttribute('src');
// img.src 的值是 '',不是undefined
// img.attributes['src'] 是 undefined

如果你直接改變DOM上的特性,attributes中 對應的屬性會有對應修改,反之亦然,不過有例外,例如 input 元素:
<input id="user" value="guest">

使用以下的程式:
document.getElementById('user').value = 'Justin';
var user1 = document.getElementById('user').value; // 值是'Justin'
var user2 = document.getElementById('user').getAttribute('value'); // 值是'guest'

要改變attribues上 的屬性,可以使用setAttribute()。 例如:
document.getElementById('user').setAttribute('value', 'Justin');
var user1 = document.getElementById('user').value; // 值'Justin'
var user2 = document.getElementById('user').getAttribute('value'); // 值'Justin'