当浏览器加载页面时,它会“读取”(另一个词:“解析”)HTML 并从中生成 DOM 对象。对于元素节点,大多数标准 HTML 属性会自动成为 DOM 对象的属性。
例如,如果标签是 <body id="page">
,则 DOM 对象具有 body.id="page"
。
但是,属性-特性映射不是一对一的!在本章中,我们将注意区分这两个概念,了解如何使用它们,何时它们相同,何时它们不同。
DOM 属性
我们已经看到了内置的 DOM 属性。有很多。但从技术上讲,没有人限制我们,如果不够,我们可以添加我们自己的属性。
DOM 节点是常规的 JavaScript 对象。我们可以更改它们。
例如,让我们在 document.body
中创建一个新属性
document.body.myData = {
name: 'Caesar',
title: 'Imperator'
};
alert(document.body.myData.title); // Imperator
我们也可以添加一个方法
document.body.sayTagName = function() {
alert(this.tagName);
};
document.body.sayTagName(); // BODY (the value of "this" in the method is document.body)
我们还可以修改内置原型,例如 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 属性和方法的行为就像常规 JavaScript 对象的属性和方法一样
- 它们可以具有任何值。
- 它们区分大小写(写
elem.nodeType
,而不是elem.NoDeTyPe
)。
HTML 属性
在 HTML 中,标签可能具有属性。当浏览器解析 HTML 以为标签创建 DOM 对象时,它会识别标准属性并从中创建 DOM 属性。
因此,当元素具有 id
或另一个标准属性时,将创建相应的属性。但如果属性是非标准的,则不会发生这种情况。
例如
<body id="test" something="non-standard">
<script>
alert(document.body.id); // test
// non-standard attribute does not yield a property
alert(document.body.something); // undefined
</script>
</body>
请注意,一个元素的标准属性对于另一个元素可能是未知的。例如,"type"
对于 <input>
(HTMLInputElement) 是标准的,但对于 <body>
(HTMLBodyElement) 不是。标准属性在相应元素类的规范中进行了描述。
在这里我们可以看到它
<body id="body" type="...">
<input id="input" type="text">
<script>
alert(input.type); // text
alert(body.type); // undefined: DOM property not created, because it's non-standard
</script>
</body>
因此,如果属性是非标准的,则不会有 DOM 属性。有没有办法访问此类属性?
当然。可以使用以下方法访问所有属性
elem.hasAttribute(name)
– 检查是否存在。elem.getAttribute(name)
– 获取值。elem.setAttribute(name, value)
– 设置值。elem.removeAttribute(name)
– 删除属性。
这些方法完全按照 HTML 中所写的内容进行操作。
还可以使用 elem.attributes
读取所有属性:属于内置 Attr 类的对象集合,具有 name
和 value
属性。
以下是读取非标准属性的演示
<body something="non-standard">
<script>
alert(document.body.getAttribute('something')); // non-standard
</script>
</body>
HTML 属性具有以下特征
- 它们的名称不区分大小写(
id
与ID
相同)。 - 它们的值始终为字符串。
以下是使用属性的扩展演示
<body>
<div id="elem" about="Elephant"></div>
<script>
alert( elem.getAttribute('About') ); // (1) 'Elephant', reading
elem.setAttribute('Test', 123); // (2), writing
alert( elem.outerHTML ); // (3), see if the attribute is in HTML (yes)
for (let attr of elem.attributes) { // (4) list all
alert( `${attr.name} = ${attr.value}` );
}
</script>
</body>
请注意
getAttribute('About')
– 此处第一个字母大写,而在 HTML 中全部小写。但这无关紧要:属性名称不区分大小写。- 我们可以将任何内容分配给属性,但它会变成字符串。因此,此处我们有
"123"
作为值。 - 包括我们设置的所有属性在内的所有属性都在
outerHTML
中可见。 attributes
集合是可迭代的,并且具有元素的所有属性(标准和非标准)作为具有name
和value
属性的对象。
属性同步
当标准属性发生更改时,相应的属性会自动更新,反之亦然(有一些例外)。
在下面的示例中,id
被修改为属性,并且我们可以看到属性也发生了更改。然后反过来也是如此
<input>
<script>
let input = document.querySelector('input');
// attribute => property
input.setAttribute('id', 'id');
alert(input.id); // id (updated)
// property => attribute
input.id = 'newId';
alert(input.getAttribute('id')); // newId (updated)
</script>
但有一些例外,例如 input.value
仅从属性 → 属性同步,但不会反向同步
<input>
<script>
let input = document.querySelector('input');
// attribute => property
input.setAttribute('value', 'text');
alert(input.value); // text
// NOT property => attribute
input.value = 'newValue';
alert(input.getAttribute('value')); // text (not updated!)
</script>
在上面的示例中
- 更改属性
value
会更新属性。 - 但属性更改不会影响属性。
该“特性”实际上可能派上用场,因为用户操作可能导致 value
更改,然后在这些操作之后,如果我们希望从 HTML 中恢复“原始”值,则它在属性中。
DOM 属性具有类型
DOM 属性并不总是字符串。例如,input.checked
属性(对于复选框)是一个布尔值
<input id="input" type="checkbox" checked> checkbox
<script>
alert(input.getAttribute('checked')); // the attribute value is: empty string
alert(input.checked); // the property value is: true
</script>
还有其他示例。style
属性是一个字符串,但 style
属性是一个对象
<div id="div" style="color:red;font-size:120%">Hello</div>
<script>
// string
alert(div.getAttribute('style')); // color:red;font-size:120%
// object
alert(div.style); // [object CSSStyleDeclaration]
alert(div.style.color); // red
</script>
不过,大多数属性都是字符串。
极少情况下,即使 DOM 属性类型为字符串,它也可能与属性不同。例如,href
DOM 属性始终是一个完整 URL,即使该属性包含相对 URL 或仅包含 #hash
。
以下是一个示例
<a id="a" href="#hello">link</a>
<script>
// attribute
alert(a.getAttribute('href')); // #hello
// property
alert(a.href ); // full URL in the form http://site.com/page#hello
</script>
如果我们需要 href
或任何其他属性的值,就像在 HTML 中编写的那样,我们可以使用 getAttribute
。
非标准属性,数据集
在编写 HTML 时,我们使用了许多标准属性。但是非标准的自定义属性呢?首先,让我们看看它们是否有用?有什么用?
有时,非标准属性用于将自定义数据从 HTML 传递到 JavaScript,或“标记”HTML 元素以供 JavaScript 使用。
像这样
<!-- mark the div to show "name" here -->
<div show-info="name"></div>
<!-- and age here -->
<div show-info="age"></div>
<script>
// the code finds an element with the mark and shows what's requested
let user = {
name: "Pete",
age: 25
};
for(let div of document.querySelectorAll('[show-info]')) {
// insert the corresponding info into the field
let field = div.getAttribute('show-info');
div.innerHTML = user[field]; // first Pete into "name", then 25 into "age"
}
</script>
它们还可以用于设置元素的样式。
例如,此处使用属性 order-state
表示订单状态
<style>
/* styles rely on the custom attribute "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
等类要好?
因为属性更易于管理。状态可以像以下一样轻松更改
// a bit simpler than removing old/adding a new class
div.setAttribute('order-state', 'canceled');
但是自定义属性可能存在一个潜在问题。如果我们出于自己的目的使用非标准属性,并且稍后标准引入了它并使其执行某些操作,该怎么办?HTML 语言是活跃的,它在不断发展,并且出现了更多属性以满足开发人员的需求。在这种情况下可能会产生意外的影响。
为了避免冲突,存在 data-* 属性。
所有以“data-”开头的属性都为程序员使用而保留。它们在 dataset
属性中可用。
例如,如果一个 elem
有一个名为 "data-about"
的属性,它可以作为 elem.dataset.about
使用。
像这样
<body data-about="Elephants">
<script>
alert(document.body.dataset.about); // Elephants
</script>
像 data-order-state
这样的多词属性会变成驼峰式:dataset.orderState
。
下面是一个重写的“订单状态”示例
<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>
// read
alert(order.dataset.orderState); // new
// modify
order.dataset.orderState = "pending"; // (*)
</script>
使用 data-*
属性是一种有效且安全的方式来传递自定义数据。
请注意,我们不仅可以读取,还可以修改数据属性。然后,CSS 会相应地更新视图:在上面的示例中,最后一行 (*)
将颜色更改为蓝色。
总结
- 属性——是写在 HTML 中的内容。
- 属性——是 DOM 对象中的内容。
一个小小的比较
属性 | 属性 | |
---|---|---|
类型 | 任何值,标准属性的类型在规范中描述 | 字符串 |
名称 | 名称区分大小写 | 名称不区分大小写 |
用于处理属性的方法是
elem.hasAttribute(name)
——检查是否存在。elem.getAttribute(name)
——获取值。elem.setAttribute(name, value)
——设置值。elem.removeAttribute(name)
——移除属性。elem.attributes
是所有属性的集合。
在大多数情况下,使用 DOM 属性是更可取的。我们应该仅在 DOM 属性不适合我们时才引用属性,例如,当我们需要确切的属性时
- 我们需要一个非标准属性。但如果它以
data-
开头,那么我们应该使用dataset
。 - 我们希望读取 HTML 中“按原样编写”的值。DOM 属性的值可能不同,例如,
href
属性始终是一个完整的 URL,我们可能希望获取“原始”值。
评论
<code>
标记,要插入多行代码,请将其包装在<pre>
标记中,要插入 10 行以上的代码,请使用沙盒 (plnkr、jsbin、codepen…)