當瀏覽器載入頁面時,它會「讀取」(另一個說法是:「解析」)HTML,並從中產生 DOM 物件。對於元素節點,大多數標準 HTML 屬性會自動成為 DOM 物件的特性。
例如,如果標籤是 <body id="page">
,則 DOM 物件有 body.id="page"
。
但屬性與屬性的對應並非一對一!在本章中,我們將注意區分這兩個概念,了解如何使用它們,以及它們何時相同,何時不同。
DOM 屬性
我們已經看過內建的 DOM 屬性。有很多。但技術上沒有人限制我們,如果還不夠,我們可以新增自己的。
DOM 節點是常規的 JavaScript 物件。我們可以修改它們。
例如,讓我們在 document.body
中建立一個新屬性
document.body.myData = {
name: 'Caesar',
title: 'Imperator'
};
alert(document.body.myData.title); // Imperator
我們也可以新增一個方法
document.body.sayTagName = function() {
alert(this.tagName);
};
document.body.sayTagName(); // BODY (the value of "this" in the method is document.body)
我們還可以修改內建原型,例如 Element.prototype
,並將新方法新增到所有元素
Element.prototype.sayHi = function() {
alert(`Hello, I'm ${this.tagName}`);
};
document.documentElement.sayHi(); // Hello, I'm HTML
document.body.sayHi(); // Hello, I'm BODY
因此,DOM 屬性和方法的行為就像常規 JavaScript 物件的屬性和方法一樣
- 它們可以有任何值。
- 它們區分大小寫(寫
elem.nodeType
,而不是elem.NoDeTyPe
)。
HTML 屬性
在 HTML 中,標籤可能具有屬性。當瀏覽器解析 HTML 以為標籤建立 DOM 物件時,它會辨識標準屬性,並從中建立 DOM 屬性。
因此,當元素具有 id
或其他標準屬性時,將建立對應的屬性。但如果屬性是非標準屬性,則不會發生這種情況。
例如
<body id="test" something="non-standard">
<script>
alert(document.body.id); // test
// non-standard attribute does not yield a property
alert(document.body.something); // undefined
</script>
</body>
請注意,一個元素的標準屬性可能對另一個元素未知。例如,"type"
是 <input>
(HTMLInputElement) 的標準屬性,但不是 <body>
(HTMLBodyElement) 的標準屬性。標準屬性在對應元素類別的規格中描述。
我們可以在這裡看到它
<body id="body" type="...">
<input id="input" type="text">
<script>
alert(input.type); // text
alert(body.type); // undefined: DOM property not created, because it's non-standard
</script>
</body>
因此,如果屬性是非標準屬性,則不會有 DOM 屬性。是否有辦法存取此類屬性?
當然。可以使用下列方法存取所有屬性
elem.hasAttribute(name)
– 檢查是否存在。elem.getAttribute(name)
– 取得值。elem.setAttribute(name, value)
– 設定值。elem.removeAttribute(name)
– 移除屬性。
這些方法的操作方式與 HTML 中寫的完全相同。
此外,可以使用 elem.attributes
讀取所有屬性:一個屬於內建 Attr 類別的物件集合,具有 name
和 value
屬性。
以下是讀取非標準屬性的範例
<body something="non-standard">
<script>
alert(document.body.getAttribute('something')); // non-standard
</script>
</body>
HTML 屬性具有下列特徵
- 其名稱不區分大小寫(
id
與ID
相同)。 - 其值永遠是字串。
以下是使用屬性的延伸範例
<body>
<div id="elem" about="Elephant"></div>
<script>
alert( elem.getAttribute('About') ); // (1) 'Elephant', reading
elem.setAttribute('Test', 123); // (2), writing
alert( elem.outerHTML ); // (3), see if the attribute is in HTML (yes)
for (let attr of elem.attributes) { // (4) list all
alert( `${attr.name} = ${attr.value}` );
}
</script>
</body>
請注意
getAttribute('About')
– 此處的第一個字母為大寫,而在 HTML 中則全部為小寫。但這無關緊要:屬性名稱不區分大小寫。- 我們可以將任何內容指定給屬性,但它會變成字串。因此,我們在此處將
"123"
作為值。 - 所有屬性(包括我們設定的屬性)都顯示在
outerHTML
中。 attributes
集合是可迭代的,並具有元素的所有屬性(標準和非標準)作為具有name
和value
屬性的物件。
屬性-屬性同步
當標準屬性變更時,對應的屬性會自動更新,反之亦然(有一些例外)。
在以下範例中,id
被修改為屬性,我們可以看到屬性也變更了。然後再反向執行相同的動作
<input>
<script>
let input = document.querySelector('input');
// attribute => property
input.setAttribute('id', 'id');
alert(input.id); // id (updated)
// property => attribute
input.id = 'newId';
alert(input.getAttribute('id')); // newId (updated)
</script>
但是有一些例外,例如 input.value
僅從屬性 → 屬性同步,但不會反向同步
<input>
<script>
let input = document.querySelector('input');
// attribute => property
input.setAttribute('value', 'text');
alert(input.value); // text
// NOT property => attribute
input.value = 'newValue';
alert(input.getAttribute('value')); // text (not updated!)
</script>
在上述範例中
- 變更屬性
value
會更新屬性。 - 但是屬性變更不會影響屬性。
此「功能」實際上可能很方便,因為使用者動作可能導致 value
變更,然後在這些動作之後,如果我們想要從 HTML 中復原「原始」值,則它會在屬性中。
DOM 屬性有型別
DOM 屬性不總是字串。例如,input.checked
屬性(對於核取方塊)是布林值
<input id="input" type="checkbox" checked> checkbox
<script>
alert(input.getAttribute('checked')); // the attribute value is: empty string
alert(input.checked); // the property value is: true
</script>
還有其他範例。style
屬性是字串,但 style
屬性是物件
<div id="div" style="color:red;font-size:120%">Hello</div>
<script>
// string
alert(div.getAttribute('style')); // color:red;font-size:120%
// object
alert(div.style); // [object CSSStyleDeclaration]
alert(div.style.color); // red
</script>
不過,大多數屬性都是字串。
即使 DOM 屬性型別是字串,但它也很少與屬性不同。例如,href
DOM 屬性始終是完整的 URL,即使屬性包含相對 URL 或僅包含 #hash
。
以下是範例
<a id="a" href="#hello">link</a>
<script>
// attribute
alert(a.getAttribute('href')); // #hello
// property
alert(a.href ); // full URL in the form http://site.com/page#hello
</script>
如果我們需要 href
或任何其他屬性的值,就像在 HTML 中寫入的那樣,我們可以使用 getAttribute
。
非標準屬性、資料集
在撰寫 HTML 時,我們使用了許多標準屬性。但是非標準的自訂屬性呢?首先,讓我們看看它們是否有用?它們的用途是什麼?
有時非標準屬性用於將自訂資料從 HTML 傳遞到 JavaScript,或用於「標記」JavaScript 的 HTML 元素。
像這樣
<!-- mark the div to show "name" here -->
<div show-info="name"></div>
<!-- and age here -->
<div show-info="age"></div>
<script>
// the code finds an element with the mark and shows what's requested
let user = {
name: "Pete",
age: 25
};
for(let div of document.querySelectorAll('[show-info]')) {
// insert the corresponding info into the field
let field = div.getAttribute('show-info');
div.innerHTML = user[field]; // first Pete into "name", then 25 into "age"
}
</script>
它們也可以用來設定元素樣式。
例如,這裡的訂單狀態屬性 order-state
被使用
<style>
/* styles rely on the custom attribute "order-state" */
.order[order-state="new"] {
color: green;
}
.order[order-state="pending"] {
color: blue;
}
.order[order-state="canceled"] {
color: red;
}
</style>
<div class="order" order-state="new">
A new order.
</div>
<div class="order" order-state="pending">
A pending order.
</div>
<div class="order" order-state="canceled">
A canceled order.
</div>
為什麼使用屬性會比有像 .order-state-new
、.order-state-pending
、.order-state-canceled
這樣的類別好?
因為屬性更方便管理。狀態可以像這樣輕鬆地變更
// a bit simpler than removing old/adding a new class
div.setAttribute('order-state', 'canceled');
但自訂屬性可能會有一個潛在的問題。如果我們為了自己的目的使用非標準屬性,而標準稍後引入了它並讓它做一些事情,會怎樣?HTML 語言是活的,它會成長,而且會出現更多屬性來滿足開發人員的需求。在這種情況下可能會產生意外的影響。
為了避免衝突,存在 data-* 屬性。
所有以「data-」開頭的屬性都保留給程式設計師使用。它們可以在 dataset
屬性中取得。
例如,如果一個 elem
有名為 "data-about"
的屬性,它可以作為 elem.dataset.about
取得。
像這樣
<body data-about="Elephants">
<script>
alert(document.body.dataset.about); // Elephants
</script>
像 data-order-state
這樣的多字屬性會變成駝峰式大小寫:dataset.orderState
。
以下是重新編寫的「訂單狀態」範例
<style>
.order[data-order-state="new"] {
color: green;
}
.order[data-order-state="pending"] {
color: blue;
}
.order[data-order-state="canceled"] {
color: red;
}
</style>
<div id="order" class="order" data-order-state="new">
A new order.
</div>
<script>
// read
alert(order.dataset.orderState); // new
// modify
order.dataset.orderState = "pending"; // (*)
</script>
使用 data-*
屬性是傳遞自訂資料的有效且安全的途徑。
請注意,我們不僅可以讀取,還可以修改資料屬性。然後 CSS 會相應地更新檢視:在上面的範例中,最後一行 (*)
將顏色變更為藍色。
摘要
- 屬性 – 是寫在 HTML 中的內容。
- 屬性 – 是 DOM 物件中的內容。
一個小小的比較
屬性 | 屬性 | |
---|---|---|
類型 | 任何值,標準屬性都有規範中描述的類型 | 字串 |
名稱 | 名稱區分大小寫 | 名稱不區分大小寫 |
處理屬性的方法有
elem.hasAttribute(name)
– 檢查是否存在。elem.getAttribute(name)
– 取得值。elem.setAttribute(name, value)
– 設定值。elem.removeAttribute(name)
– 移除屬性。elem.attributes
是所有屬性的集合。
在大部分情況下,使用 DOM 屬性較佳。我們應僅在 DOM 屬性不適合我們時才參照屬性,例如,當我們需要確切的屬性時
- 我們需要一個非標準屬性。但如果它以
data-
開頭,則我們應使用dataset
。 - 我們想讀取 HTML 中「如寫」的值。DOM 屬性的值可能不同,例如,
href
屬性永遠都是一個完整的 URL,而我們可能想要取得「原始」值。
留言
<code>
標籤,若要插入多行,請將它們包在<pre>
標籤中,若要插入超過 10 行,請使用沙盒 (plnkr、jsbin、codepen…)