當浏覽器加載頁面時,它會“讀取”(或者稱之為:“解析”)HTML 并從中生成 DOM 對象。對于元素節點,大多數标準的 HTML 特性(attributes)會自動變成 DOM 對象的屬性(properties)。(譯注:attribute 和 property 兩詞意思相近,為作區分,全文将 attribute 譯為“特性”,property 譯為“屬性”,請讀者注意區分。)
概述
當我們書寫 HTML 代碼的時候,我們為 HTML 元素設定特性 ,例如:
<input id="name"value="justjavac" />
我們寫了一個
input
标簽,并給他定義了 2 個特性 (
id
和
value
)。當浏覽器解析這段代碼的時候,會把 html 源碼解析為 DOM 對象,确切的說是解析為
HTMLInputElement
對象。
HTMLInputElement
的繼承關系是:
HTMLInputElement
↓
HTMLElement
↓
Element
↓
Node
↓
EventTarget
↓
Object
通過檢視文檔會發現,
HTMLInputElement
的原型上定義了很多屬性和方法,例如
form
,
name
,
type
,
alt
,
checked
,
src
,
value
等等,還有從
HTMLElement
繼承來的
id
,
title
,
clientTop
等等。
如果仔細找找,就不難發現其中就有我們為
input
标簽定義的特性:
id
和
value
。當浏覽器解析網頁時,将 HTML 特性映射為了 DOM 屬性。
而
Element
類還有一個
attributes
屬性,裡面包含了所有的特性。
但是,HTML attribute 和 DOM property 并不總是一對一的關系。
1. DOM 屬性
當浏覽器解析完 HTML 後,生成的 DOM 是一個繼承自 Object 的正常 JavaScript 對象,是以我們可以像操作任何 JS 對象那樣來操作 DOM 對象。
const el = document.getElementById('name')
el.foo = 'bar'el.user = { name: 'jjc', age: '18'}
也可以為其添加方法。如果你想給每個 html 元素都添加屬性或方法,甚至可以直接修改
Element.prototype
,不過我們不推薦這麼做。
2. HTML 特性
和 DOM 屬性類似,除了那些規範裡定義的标準特性外,HTML 也可以添加非标準的屬性,例如:
<input id="name" value="justjavac" foo="bar" />
當 HTML 特性映射為 DOM 屬性時,隻映射标準屬性,通路非标準屬性将得到
undefined
。
const el = document.getElementById('name')
el.foo === undefine
好在 DOM 對象也提供了操作特性的 API:
-
– 判斷某個特性是否存在Element.hasAttribute(name)
-
) – 擷取指定特性的值elem.getAttribute(name
-
– 設定指定特性的值elem.setAttribute(name, value)
-
– 移除指定特性elem.removeAttribute(name)
以上 API 定義在
Element
上。
根據 HTML 規範,标簽以及特性名是不區分大小寫的,是以以下代碼是一樣的:
el.getAttribute('id')
el.getAttribute('ID')
el.getAttribute('iD')
并且,特性永遠都是字元串或
null
。如果我們為特性設定非字元串的值,則引擎會将此值轉換為字元串。屬性是具有類型的:
el.getAttribute('checked') === '' // 特性是字元串
el.checked === false // 屬性是 boolean 類型的值
el.getAttribute('style') === 'color:blue' // 特性是字元串
typeof el.style === 'object' // 屬性是 CSSStyleDeclaration 對象
即使都是字元串,屬性和特性也可能不同,有一個例外就是
href
:
el.getAttribute('href') === '#tag' // 特性原樣傳回 html 設定的值
el.href === 'http://jjc.fun#tag' // 屬性傳回解析後的完整 uri
3. 特性和屬性的同步
當标準的特性更新時,對應的屬性也會更新;反之亦然。
但是
input.value
的同步是單向的,隻是
attribute --> property
。當修改特性時,屬性也會更新;但是修改屬性後,特性卻還是原值。
el.setAttribute('value', 'jjc'); // 修改特性
el.value === 'jjc' // 屬性也更新了
el.value = 'newValue'; // 修改屬性
el.getAttribute('value')) === 'jjc' // 特性沒有更新
4. 非标準特性
非标準 HTML 特性并不會自動映射為 DOM 屬性。當我們使用
data-
開頭的特性時,會映射到 DOM 的 dataset 屬性。中劃線格式會變成駝峰格式:
el.setAttribute('data-my-name', 'jjc');
el.dataset.myName === 'jjc';
el.setAttribute('data-my-AGE', 18);
el.dataset.myAge === '18';
自定義特性 VS 非規範特性
HTML 允許我們自定義标簽,也可以擴充标簽的特性,但是我們推薦使用已經進入 HTML5 規範的自定義特性
data-*
。比如我們想為
div
标簽增加一個
age
特性,我們可以有 2 種選擇:
<div age="18">justjavac</div>
<div data-age="18">justjavac</div>
雖然第一種代碼更短,但是卻有一個潛在的風險。因為 HTML 規範是一直發展變化的,也許在未來的某個版本中,
age
被添加進了标準特性裡面,這将會引起潛在的 bug。
結論
HTML attribute DOMproperty
值永遠是字元串或 null 值可以是任意合法 js 類型
大小寫不敏感 大小寫敏感
不存在時傳回 null 不存在時傳回 undefined
對于 href, 傳回 html 設定的值 對于 href 傳回解析後的完整 url
更新 value, 屬性 也更新更新 value, 特性不更新