当浏览器加载页面时,它会“读取”(或者称之为:“解析”)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>

注意:

操作 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 忽略大小写