W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗(yàn)值獎勵
當(dāng)瀏覽器加載頁面時,它會“讀取”(或者稱之為:“解析”)HTML 并從中生成 DOM 對象。對于元素節(jié)點(diǎn),大多數(shù)標(biāo)準(zhǔn)的 HTML 特性(attributes)會自動變成 DOM 對象的屬性(properties)。(譯注:attribute 和 property 兩詞意思相近,為作區(qū)分,全文將 attribute 譯為“特性”,property 譯為“屬性”,請讀者注意區(qū)分。)
例如,如果標(biāo)簽是 <body id="page">
,那么 DOM 對象就會有 body.id="page"
。
但特性—屬性映射并不是一一對應(yīng)的!在本章,我們將帶領(lǐng)你一起分清楚這兩個概念,了解如何使用它們,了解它們何時相同何時不同。
我們已經(jīng)見過了內(nèi)建 DOM 屬性。它們數(shù)量龐大。但是從技術(shù)上講,沒有人會限制我們,如果我們覺得這些 DOM 還不夠,我們可以添加我們自己的。
DOM 節(jié)點(diǎn)是常規(guī)的 JavaScript 對象。我們可以更改它們。
例如,讓我們在 document.body
中創(chuàng)建一個新的屬性:
document.body.myData = {
name: 'Caesar',
title: 'Imperator'
};
alert(document.body.myData.title); // Imperator
我們也可以像下面這樣添加一個方法:
document.body.sayTagName = function() {
alert(this.tagName);
};
document.body.sayTagName(); // BODY(這個方法中的 "this" 的值是 document.body)
我們還可以修改內(nèi)建屬性的原型,例如修改 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 屬性和方法的行為就像常規(guī)的 Javascript 對象一樣:
elem.nodeType
?,而不是 ?elem.NoDeTyPe
?)。在 HTML 中,標(biāo)簽可能擁有特性(attributes)。當(dāng)瀏覽器解析 HTML 文本,并根據(jù)標(biāo)簽創(chuàng)建 DOM 對象時,瀏覽器會辨別 標(biāo)準(zhǔn)的 特性并以此創(chuàng)建 DOM 屬性。
所以,當(dāng)一個元素有 id
或其他 標(biāo)準(zhǔn)的 特性,那么就會生成對應(yīng)的 DOM 屬性。但是非 標(biāo)準(zhǔn)的 特性則不會。
例如:
<body id="test" something="non-standard">
<script>
alert(document.body.id); // test
// 非標(biāo)準(zhǔn)的特性沒有獲得對應(yīng)的屬性
alert(document.body.something); // undefined
</script>
</body>
請注意,一個元素的標(biāo)準(zhǔn)的特性對于另一個元素可能是未知的。例如 "type"
是 <input>
的一個標(biāo)準(zhǔn)的特性(HTMLInputElement),但對于 <body>
(HTMLBodyElement)來說則不是。規(guī)范中對相應(yīng)元素類的標(biāo)準(zhǔn)的屬性進(jìn)行了詳細(xì)的描述。
這里我們可以看到:
<body id="body" type="...">
<input id="input" type="text">
<script>
alert(input.type); // text
alert(body.type); // undefined:DOM 屬性沒有被創(chuàng)建,因?yàn)樗皇且粋€標(biāo)準(zhǔn)的特性
</script>
</body>
所以,如果一個特性不是標(biāo)準(zhǔn)的,那么就沒有相對應(yīng)的 DOM 屬性。那我們有什么方法來訪問這些特性嗎?
當(dāng)然。所有特性都可以通過使用以下方法進(jìn)行訪問:
elem.hasAttribute(name)
? —— 檢查特性是否存在。elem.getAttribute(name)
? —— 獲取這個特性值。elem.setAttribute(name, value)
? —— 設(shè)置這個特性值。elem.removeAttribute(name)
? —— 移除這個特性。這些方法操作的實(shí)際上是 HTML 中的內(nèi)容。
我們也可以使用 elem.attributes
讀取所有特性:屬于內(nèi)建 Attr 類的對象的集合,具有 name
和 value
屬性。
下面是一個讀取非標(biāo)準(zhǔn)的特性的示例:
<body something="non-standard">
<script>
alert(document.body.getAttribute('something')); // non-standard
</script>
</body>
HTML 特性有以下幾個特征:
id
? 與 ?ID
? 相同)。下面是一個使用特性的擴(kuò)展示例:
<body>
<div id="elem" about="Elephant"></div>
<script>
alert( elem.getAttribute('About') ); // (1) 'Elephant',讀取
elem.setAttribute('Test', 123); // (2) 寫入
alert( elem.outerHTML ); // (3) 查看特性是否在 HTML 中(在)
for (let attr of elem.attributes) { // (4) 列出所有
alert( `${attr.name} = ${attr.value}` );
}
</script>
</body>
請注意:
getAttribute('About')
? —— 這里的第一個字母是大寫的,但是在 HTML 中,它們都是小寫的。但這沒有影響:特性的名稱是大小寫不敏感的。"123"
?。outerHTML
? 中都是可見的。attributes
? 集合是可迭代對象,該對象將所有元素的特性(標(biāo)準(zhǔn)和非標(biāo)準(zhǔn)的)作為 ?name
? 和 ?value
? 屬性存儲在對象中。當(dāng)一個標(biāo)準(zhǔn)的特性被改變,對應(yīng)的屬性也會自動更新,(除了幾個特例)反之亦然。
在下面這個示例中,id
被修改為特性,我們可以看到對應(yīng)的屬性也發(fā)生了變化。然后反過來也是同樣的效果:
<input>
<script>
let input = document.querySelector('input');
// 特性 => 屬性
input.setAttribute('id', 'id');
alert(input.id); // id(被更新了)
// 屬性 => 特性
input.id = 'newId';
alert(input.getAttribute('id')); // newId(被更新了)
</script>
但這里也有些例外,例如 input.value
只能從特性同步到屬性,反過來則不行:
<input>
<script>
let input = document.querySelector('input');
// 特性 => 屬性
input.setAttribute('value', 'text');
alert(input.value); // text
// 這個操作無效,屬性 => 特性
input.value = 'newValue';
alert(input.getAttribute('value')); // text(沒有被更新?。?</script>
在上面這個例子中:
value
? 會更新屬性。這個“功能”在實(shí)際中會派上用場,因?yàn)橛脩粜袨榭赡軙?dǎo)致 value
的更改,然后在這些操作之后,如果我們想從 HTML 中恢復(fù)“原始”值,那么該值就在特性中。
DOM 屬性不總是字符串類型的。例如,input.checked
屬性(對于 checkbox 的)是布爾型的。
<input id="input" type="checkbox" checked> checkbox
<script>
alert(input.getAttribute('checked')); // 特性值是:空字符串
alert(input.checked); // 屬性值是:true
</script>
還有其他的例子。style
特性是字符串類型的,但 style
屬性是一個對象:
<div id="div" style="color:red;font-size:120%">Hello</div>
<script>
// 字符串
alert(div.getAttribute('style')); // color:red;font-size:120%
// 對象
alert(div.style); // [object CSSStyleDeclaration]
alert(div.style.color); // red
</script>
盡管大多數(shù) DOM 屬性都是字符串類型的。
有一種非常少見的情況,即使一個 DOM 屬性是字符串類型的,但它可能和 HTML 特性也是不同的。例如,href
DOM 屬性一直是一個 完整的 URL,即使該特性包含一個相對路徑或者包含一個 #hash
。
這里有一個例子:
<a id="a" href="#hello">link</a>
<script>
// 特性
alert(a.getAttribute('href')); // #hello
// 屬性
alert(a.href ); // http://site.com/page#hello 形式的完整 URL
</script>
如果我們需要 href
特性的值,或者其他與 HTML 中所寫的完全相同的特性,則可以使用 getAttribute
。
當(dāng)編寫 HTML 時,我們會用到很多標(biāo)準(zhǔn)的特性。但是非標(biāo)準(zhǔn)的,自定義的呢?首先,讓我們看看它們是否有用?用來做什么?
有時,非標(biāo)準(zhǔn)的特性常常用于將自定義的數(shù)據(jù)從 HTML 傳遞到 JavaScript,或者用于為 JavaScript “標(biāo)記” HTML 元素。
像這樣:
<!-- 標(biāo)記這個 div 以在這顯示 "name" -->
<div show-info="name"></div>
<!-- 標(biāo)記這個 div 以在這顯示 "age" -->
<div show-info="age"></div>
<script>
// 這段代碼找到帶有標(biāo)記的元素,并顯示需要的內(nèi)容
let user = {
name: "Pete",
age: 25
};
for(let div of document.querySelectorAll('[show-info]')) {
// 在字段中插入相應(yīng)的信息
let field = div.getAttribute('show-info');
div.innerHTML = user[field]; // 首先 "name" 變?yōu)?Pete,然后 "age" 變?yōu)?25
}
</script>
它們還可以用來設(shè)置元素的樣式。
例如,這里使用 order-state
特性來設(shè)置訂單狀態(tài):
<style>
/* 樣式依賴于自定義特性 "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
這些樣式類要好?
因?yàn)樘匦灾蹈菀坠芾怼N覀兛梢暂p松地更改狀態(tài):
// 比刪除舊的或者添加一個新的類要簡單一些
div.setAttribute('order-state', 'canceled');
但是自定義的特性也存在問題。如果我們出于我們的目的使用了非標(biāo)準(zhǔn)的特性,之后它被引入到了標(biāo)準(zhǔn)中并有了其自己的用途,該怎么辦?HTML 語言是在不斷發(fā)展的,并且更多的特性出現(xiàn)在了標(biāo)準(zhǔn)中,以滿足開發(fā)者的需求。在這種情況下,自定義的屬性可能會產(chǎn)生意料不到的影響。
為了避免沖突,存在 data-* 特性。
所有以 “data-” 開頭的特性均被保留供程序員使用。它們可在 dataset
屬性中使用。
例如,如果一個 elem
有一個名為 "data-about"
的特性,那么可以通過 elem.dataset.about
取到它。
像這樣:
<body data-about="Elephants">
<script>
alert(document.body.dataset.about); // Elephants
</script>
像 data-order-state
這樣的多詞特性可以以駝峰式進(jìn)行調(diào)用:dataset.orderState
。
這里是 “order state” 那個示例的重構(gòu)版:
<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>
// 讀取
alert(order.dataset.orderState); // new
// 修改
order.dataset.orderState = "pending"; // (*)
</script>
使用 data-*
特性是一種合法且安全的傳遞自定義數(shù)據(jù)的方式。
請注意,我們不僅可以讀取數(shù)據(jù),還可以修改數(shù)據(jù)屬性(data-attributes)。然后 CSS 會更新相應(yīng)的視圖:在上面這個例子中的最后一行 (*)
將顏色更改為了藍(lán)色。
簡略的對比:
屬性 | 特性 | |
---|---|---|
類型 | 任何值,標(biāo)準(zhǔn)的屬性具有規(guī)范中描述的類型 | 字符串 |
名字 | 名字(name)是大小寫敏感的 | 名字(name)是大小寫不敏感的 |
操作特性的方法:
elem.hasAttribute(name)
? —— 檢查是否存在這個特性。elem.getAttribute(name)
? —— 獲取這個特性值。elem.setAttribute(name, value)
? —— 設(shè)置這個特性值。elem.removeAttribute(name)
? —— 移除這個特性。elem.attributes
? —— 所有特性的集合。在大多數(shù)情況下,最好使用 DOM 屬性。僅當(dāng) DOM 屬性無法滿足開發(fā)需求,并且我們真的需要特性時,才使用特性,例如:
data-
? 開頭,那么我們應(yīng)該使用 ?dataset
?。href
? 屬性一直是一個 完整的 URL,但是我們想要的是“原始的”值。編寫代碼,從文檔(document)中獲取帶有 data-widget-name
特性(attribute)的元素,并讀取它的值。
<!DOCTYPE html>
<html>
<body>
<div data-widget-name="menu">Choose the genre</div>
<script>
/* your code */
</script>
</body>
</html>
<!DOCTYPE html>
<html>
<body>
<div data-widget-name="menu">Choose the genre</div>
<script>
// 獲取它
let elem = document.querySelector('[data-widget-name]');
// 讀取值
alert(elem.dataset.widgetName);
// 或
alert(elem.getAttribute('data-widget-name'));
</script>
</body>
</html>
通過修改 style
屬性,將所有外部鏈接變?yōu)槌壬?
如果一個鏈接是外部的:
href
? 中包含 ?://
?http://internal.com
? 開頭。例如:
<a name="list">the list</a>
<ul>
<li><a rel="external nofollow" target="_blank" >http://google.com</a></li>
<li><a href="/tutorial">/tutorial.html</a></li>
<li><a href="local/path">local/path</a></li>
<li><a href="ftp://ftp.com/my.zip" rel="external nofollow" target="_blank" >ftp://ftp.com/my.zip</a></li>
<li><a rel="external nofollow" target="_blank" >http://nodejs.org</a></li>
<li><a rel="external nofollow" target="_blank" >http://internal.com/test</a></li>
</ul>
<script>
// 為單個鏈接設(shè)置樣式
let link = document.querySelector('a');
link.style.color = 'orange';
</script>
結(jié)果應(yīng)該是:
首先,我們需要找到所有外部鏈接。
這里有兩種方式。
第一種是使用 document.querySelectorAll('a')
找到所有鏈接,然后過濾出我們需要的部分:
let links = document.querySelectorAll('a');
for (let link of links) {
let href = link.getAttribute('href');
if (!href) continue; // 沒有特性
if (!href.includes('://')) continue; // 沒有協(xié)議
if (href.startsWith('http://internal.com')) continue; // 內(nèi)部的
link.style.color = 'orange';
}
請注意:我們用的是 link.getAttribute('href')
。而不是 link.href
,因?yàn)槲覀冃枰氖莵碜?HTML 的值。
……另一種更簡單的方法,是使用 CSS 選擇器進(jìn)行檢查:
// 查找所有 href 中包含 :// 的鏈接
// 但 href 不是以 http://internal.com 開頭
let selector = 'a[href*="://"]:not([href^="http://internal.com"])';
let links = document.querySelectorAll(selector);
links.forEach(link => link.style.color = 'orange');
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報電話:173-0602-2364|舉報郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: