五、Attributes 和 properties
当浏览器加载页面时,它会解析 html 并形成 DOM 对象,对于元素节点,大多数标准的 HTML 特性(attributes)会自动变成 DOM 对象的属性(properties)。
比如:如果<body id='body'>那么 DOM 对象中就会存在document.body.id=‘body’
但是特性跟属性的映射并非一一对应的。
5.1 DOM 属性 properties
DOM 节点的属性和方法是常规的 JavaScript 对象:
- 它们可以有很多值
- 它们大小写敏感
我们可以自由地增加 DOM 属性
比如,我们可以给 body 增加属性和方法
javascript1document.body.data = { 2 name: 'body', 3} 4document.body.sayHi = function () { 5 console.log('hi,my name is ' + this.data.name) 6} 7document.body.sayHi()
也可以在原型上添加方法
javascript1Element.prototype.sayHi = function () { 2 console.log('hi,my name is ' + this.tagName) 3} 4 5document.body.sayHi() // hi,my name is BODY
5.2 HTML 特性 attributes
HTML 中,标签可能拥有特性(attributes)。当浏览器解析 HTML 文本,并根据标签创建 DOM 对象时,浏览器会辨别 标准的 特性并以此创建 DOM 属性。
当一个 HTML 标签拥有标准特性时,会生成对应的 DOM 属性。但是非标准特性就不会
html1<div id="elem" another="another"></div> 2<script> 3 // 标准特性会获得id属性 4 console.log(elem.id) // elem 5 // 非标准特性不会获得another属性 6 console.log(elem.another) // undefined 7</script>
不同的 HTML 标签有不一样的标准特性,比如<input>拥有 type 特性,但是<body>就不拥有这个标准特性。
如果一个特性并不是标准的,我们就无法通过 DOM 的 API 去获取这些特性,以下方法可以帮助我们获取这些特性:
elem.hasAttribute(name)—— 检查是否有这个特性elem.getAttribute(name)—— 获取这个特性值elem.setAttribute(name,value)—— 设置特性值elem.removeAttribute(name)—— 移除这个特性值elem.attributes—— 读取所有特性:属于内建 Attr 类的对象的集合,具有 name 和 value 属性。
这些方法操作的是 HTML 的内容。以下是一个示例
html1<body something="non-standard"> 2 <script> 3 alert(document.body.getAttribute('something')) // 非标准的 4 </script> 5</body>
HTML 特性有以下特征:
- 它们的名字对大小写不敏感(id 与 ID 相同)
- 它们的值总是字符串类型的
html1<div id="elem" about="Elephant"></div> 2 3<script> 4 console.log(elem.getAttribute('About')) // (1) 'Elephant',读取 5 6 elem.setAttribute('Test', 123) // (2) 写入 7 8 console.log(elem.outerHTML) // (3) <div id="elem" about="Elephant" test="123"></div> 9 10 for (let attr of elem.attributes) { 11 // (4) 列出所有 12 console.log(`${attr.name} = ${attr.value}`) 13 } 14</script>
-
在第(1)步中,我们读取的是”About”,在 HTMl 中是 about,但这并不影响读取它的值,这说明对大小写不敏感
-
我们可以将任何东西都设置成特性的值,但最终会变成字符串,第(2)步写入的 123 会变成字符串
-
所有特性,在我们
outerHTML后都是可见的 -
attributes是可迭代的集合,该对象将所有特性(标准和非标准)都作为name和value属性存储在对象中
5.3 属性-特性同步
当一个标准特性被修改时,对应的属性也会自动更新(也有例外)。
html1<input type="text" /> 2 3<script> 4 const elem = document.querySelector('input') 5 // 设置特性 6 elem.setAttribute('id', 'id') 7 // 查看属性 8 console.log(elem.id) // id 特性=>属性 更新了 9 //设置属性 10 elem.id = 'newId' 11 //查看特性 12 console.log(elem.getAttribute('id')) // newId 属性=>特性 更新了 13</script>
上面的例子中,我们设置 input 的标准特性 id,从属性到特性,再从特性到属性都是可以同步的。
但是也有一些例外。input 的 value 只能从特性同步到属性,反过来就不行:
javascript1// 设置value特性为123 2elem.setAttribute('value', '123') 3console.log(elem.value) // 123 说明特性=>属性成功 4 5// 设置value属性为456 6elem.value = '456' 7console.log(elem.getAttribute('value')) // 123 属性=>特性不成功
从上面的例子中:
- 我们设置了 value 特性,会发现属性更新了
- 属性的更改不会影响特性
这个情况在实际开发中可能有效,比如用户更改了 value 值,如果想要恢复成默认值,就可以直接从特性中取原始值。
5.4 属性不一定与特性保持一致
相对于特性的值总是字符串类型,DOM 属性是多类型的并且不一定与特性是一致的。例如,input.checked 是布尔型的,checked 特性则是空字符串
html1<input type="text" checked /> 2 3<script> 4 const elem = document.querySelector('input') 5 console.log(elem.getAttribute('checked')) // checked特性是空字符串 6 7 console.log(elem.checked) // true 8</script>
style 特性是字符串型的,但 style 属性却是对象类型
html1<div style="background: chocolate; border: 1px solid red;"></div> 2 3<script> 4 const elem = document.querySelector('div') 5 console.log(elem.getAttribute('style')) // background: chocolate; border: 1px solid red; 6 7 alert(elem.style) //[object CSSStyleDeclaration] 8 console.log(elem.style.color) // chocolate 9</script>
还有一种情况,两者都是字符串类型,但是值却是不一样的。
下面的 a 元素的 href 特性与 href 属性就不一致,href 的 DOM 属性一直是一个完整的 URL,即使该特性包含一个相对路径或者包含一个#hash
html1<a id="a" href="#location"></a> 2 3<script> 4 console.log(a.href) // https://1mgz6.csb.app/#location 完整形式的url 5 console.log(a.getAttribute('href')) // #location 6</script>
5.5 非标准特性,dataset
我们有时候会用到自定义特性,这是一种非标准的特性,常常被应用于将自定义数据从 HTML 传到 JavaScript。
html1<!-- 标记这个 div 以在这显示 "name" --> 2<div show-info="name"></div> 3<!-- 标记这个 div 以在这显示 "age" --> 4<div show-info="age"></div> 5 6<script> 7 const user = { name: 'qiuyanxi', age: 10 } 8 let divs = document.body.querySelectorAll('div[show-info]') //获取所有标记了show-info属性的元素 9 for (let div of divs) { 10 const field = div.getAttribute('show-info') // 获取要标记的字段 11 div.innerHTML = user[field] //按照要标记的字段来展示对应内容 12 } 13</script>
上面的代码可以按照自定义的特性来展示特性值映射的内容。最终页面上会显示姓名跟年龄。
还可以使用特性来设置元素的样式
html1<style> 2 /* 样式依赖于自定义特性 "order-state" */ 3 .order[order-state='new'] { 4 color: green; 5 } 6 7 .order[order-state='pending'] { 8 color: blue; 9 } 10 11 .order[order-state='canceled'] { 12 color: red; 13 } 14</style> 15 16<div class="order" order-state="new">A new order.</div> 17 18<div class="order" order-state="pending">A pending order.</div> 19 20<div class="order" order-state="canceled">A canceled order.</div>
使用自定义特性来替代.order-state-new,.order-state-pending,.order-state-canceled等类样式,会更加容易管理,我们可以轻松更改状态。
javascript1// 比删除旧的或者添加一个新的类要简单一些 2div.setAttribute('order-state', 'new')
但是使用自定义特性有一个问题,随着 HTML 标准的延伸,很有可能我们曾经用过的自定义特性会变成标准。这样的话可能就会产生意料不到的影响。
为了避免冲突,我们可以使用data-*特性
所有以data-开头的特性均会被保留以供程序员使用,它们都在 dataset 属性中。
比如,data-about特性可以通过 DOM 属性dataset.about读取
html1<div id="div" data-about="me"></div> 2<script> 3 console.log(div.dataset.about) // me 4</script>
像data-order-state这样的多词特性可以用驼峰式dataset.orderState调用
html1<div id="div" data-order-state="new"></div> 2<script> 3 console.log(div.dataset.orderState) // new 4 div.dataset.orderState = 'old' //不仅可以读取,还可以修改,修改后的结果也是实时同步到特性中的 5 console.log(div.outerHTML) // <div id="div" data-order-state="old"></div> 6</script>
使用data-*是一种合法且安全的传递自定义数据的方式,我们不仅可以读取数据,还可以修改数据。修改后的结果会同步。
5.6 小结
- 特性(attribute):写在 HTML 中的内容
- 属性(property):写在 DOM 对象中的内容
当浏览器解析 HTML 后会将大多数特性转化为 DOM 属性,但不一定是一一对应的映射关系。
- 非标准特性不能转化为 DOM 属性
- 标准特性转化后也不一定与 DOM 属性保持一致
- 特性一般是字符串类型,而 DOM 属性则是多类型的,可以是任何值。
- 即使特性和属性同为字符串,也不一定一致。例如
href属性一直是一个 完整的 URL,但是href特性可能是“原始的”值。 - 特性大小写不敏感,属性大小写敏感
- 标准特性与 DOM 属性大多数情况下是同步的,即修改了特性会影响属性,修改属性会同步影响特性,但有些属性修改并不会同步修改特性,比如 input 的 value 值
- 为了避免非标准特性未来与标准特性重复造成影响,我们可以使用
data-*特性,这个非标准特性可以通过dataset属性获取内容。不仅可以读取数据,还可以修改数据,特性跟属性之间会同步数据。
除此之外,我们还可以使用以下 API 去获取 HTML 特性
elem.hasAttribute(name)—— 检查是否有这个特性elem.getAttribute(name)—— 获取这个特性值elem.setAttribute(name,value)—— 设置特性值elem.removeAttribute(name)—— 移除这个特性值elem.attributes—— 读取所有特性:属于内建 Attr 类的对象的集合,具有 name 和 value 属性。
在大多数情况下 ,最好使用 DOM 属性,当 DOM 属性无法满足需求时,可以考虑使用特性。