当浏览器加载页面时,它会“读取”(或者称之为:“解析”)HTML 并从中生成 DOM 对象。对于元素节点,大多数标准的 HTML 特性(attributes)会自动变成 DOM 对象的属性(properties)
但特性与属性映射并不是一一对应的!
- 特性(attribute)—— 写在 HTML 中的内容
- 属性(property)—— DOM 对象中的属性
attribute
attribute 自动映射为 property
浏览器解析 HTML 文本,并根据标签创建 DOM 对象,会辨别标准的特性(attribute)并以此创建 DOM 属性(properties),但是非标准的特性则不会:
<body id="test" something="non-standard">
<script>
// 标准特性会添加到 DOM 属性
console.log(document.body.id) // test
// 非标准的特性不会
console.log(document.body.something); // undefined
</script>
</body>
注意:
- 一个元素的标准特性对于另一个元素可能是未知的
- 例如
"type"
是<input>
的一个标准特性(HTMLInputElement),但对于<body>
(HTMLBodyElement)来说则不是
- 例如
- attribute 映射的 properties 值并非总是相同的
操作 attribute
可以使用 elem.attributes
读取所有 attribute,属于内建 Attr 类的对象的集合,具有 name
和 value
属性:
for (let attr of elem.attributes) {
console.log(attr.name, attr.value)
}
所有 attribute 都可以通过使用以下方法进行访问:
elem.hasAttribute(key)
—— 检查特性是否存在elem.getAttribute(key)
—— 获取这个特性值elem.setAttribute(key, value)
—— 设置这个特性值elem.removeAttribute(name)
—— 移除这个特性
其中,key 和 value 都是字符串类型(因为其操作的实际上是 HTML 中的内容),不是字符串的话会进行 隐式类型转换
即:
- key 是字符串,大小写不敏感(实际存储的都是小写)
- value 是字符串类型
同时还有相应的 elem.*AttributeNS()
方法,用于 命名空间。
attribute、property 同步
当一个标准的 attribute 被改变,对应的 property 也会自动更新(除了几个特例),反之亦然。
<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
只能从 attribute 同步到 property,反过来则不行:
<input>
<script>
let input = document.querySelector('input');
// 特性 => 属性
input.setAttribute('value', 'text');
alert(input.value); // text
// 这个操作无效,属性 => 特性
input.value = 'newValue';
alert(input.getAttribute('value')); // text(没有被更新!)
</script>
property
DOM 节点是 JS 对象
DOM 节点是常规的 JS 对象,可以向普通 JS 对象一样操作:
// 添加属性
document.body.myData = {
name: 'Caesar',
title: 'Imperator'
}
console.log(document.body.myData.title) // 'Imperator'
// 添加方法
document.body.sayTagName = function() {
alert(this.tagName)
}
document.body.sayTagName() // BODY
// 修改原型
Element.prototype.sayHi = function() {
alert(`Hello, I'm ${this.tagName}`)
}
document.documentElement.sayHi() // Hello, I'm HTML
document.body.sayHi() // Hello, I'm BODY
// ...
- key 是字符串,大小写敏感
- value 是任意类型,标准的属性具有规范中描述的类型
property 不同与 attribute
property 不总是字符串类型。例如,input.checked
属性(type="checkbox"
)是布尔型:
<input id="input" type="checkbox" checked>checkbox</input>
<script>
console.log(input.getAttribute('checked')) // ""
console.log(input.checked) // true
</script>
还有其他的例子:style
属性是一个对象:
<div id="div" style="color:red;font-size:120%">Hello</div>
<script>
// 字符串
console.log(div.getAttribute('style')) // "color:red;font-size:120%"
// 对象
console.log(div.style) // [object CSSStyleDeclaration]
console.log(div.style.color) // "red"
</script>
还有 property 是字符串类型,但它和 attribute 不同。例如,href
property 一直是一个完整的 URL:
<a id="a" href="#hello">link</a>
<script>
console.log(a.getAttribute('href')) // "#hello"
console.log(a.href) // "http://localhost:8080/page#hello"
</script>
自定义属性:data-*
所有以 “data-
” 开头的 attribute 均被保留供程序员使用。它们可在 dataset
property 中使用:
<div id="order" data-order-state="123">A new order.</div>
<script>
// 读取
console.log(order.dataset.orderState) // "123"
// 修改
order.dataset.orderState = "pending"
</script>
使用 data-*
特性是一种合法且安全的传递自定义数据的方式。
注意:
- 可读可写
- key 转变规则:
- attribute:
data-order-state
;property:dataset.orderState
- 推荐写法:attribute 中划线分割,property 小驼峰
- attribute:
data-orderState
;property:dataset.orderstate
- 不推荐写法:attribute key 忽略大小写
- attribute: