原文链接:https://javascript.info/dom-navigation,translate with ❤️ by zhangbao.
DOM 允许对元素及其内容进行任何操作,但首先我们需要获得相应的 DOM 对象,将其存入一个变量,然后我们才能对其进行修改。
所有的 DOM 操作起始于 document
对象,使用它可以获得文档中的任何节点。
下面这张图片展示了在 DOM 节点之间遍历的方法:
下面针对图片里的内容,进行详细说明。
最顶部:documentElement 和 body
从 document
对象上可以直接获取的节点元素有:
** = document.documentElement**
最顶部的节点是 document.documentElement
,对应 <html>
标签。
** = document.body**
节点 document.body
,对应 <body>
标签。
** = document.head**
节点 document.head
,对应 <head>
标签
docuemnt.body
** 的结果可能是 ****null**
脚本执行时,只能访问到脚本运行处之前的 DOM 节点,如果访问了之后的节点,返回结果就为
null
。举个例子:我们在
<head>
标签中书写代码,获取document.body
时,就会得到null
。
<html>
<head>
<script>
alert( '在 <head> 中: ' + document.body ); // null。还没到能访问 <body> 的时候
</script>
</head>
<body>
<script>
alert( '在 <body> 中: ' + document.body ); // HTMLBodyElement。现在有了
</script>
</body>
</html>
在 DOM 世界,
null
表示“不存在”在 DOM 中,null`` 意味着“不存在”或者“没有这个节点”。
孩子:childNodes,firstChild 和 lastChild
从现在开始,我们将使用两种术语:
孩子节点(或叫孩子):指元素的直接子元素。比如,
<head>
和<body>
是<html>
元素的孩子节点。后代:元素中嵌套的所有后代元素,包括孩子、孩子的孩子……
例如,这里的 <body>
包含孩子 <div>
和 <ul>
(还有一些空文本节点):
<html>
<body>
<div>Begin</div>
<ul>
<li>
<b>Information</b>
</li>
</ul>
</body>
</html>
如果我们要查找 <body>
的所有后代元素,除了 <div>
和 <ul>
,<ul>
的中的嵌套元素像 <li>
(<ul>
的孩子)和 <b>
(<li>
的孩子)都包含在内。
childNodes
获取的元素集合,是某元素下的所有孩子节点,包括文本节点。
下面例子中展示了 document.body
下的孩子节点:
<html>
<body>
<div>Begin</div>
<ul>
<li>Information</li>
</ul>
<div>End</div>
<script>
for (let i = 0; i < document.body.childNodes.length; i++) {
console.log( document.body.childNodes[i] ); // Text, DIV, Text, UL, ..., SCRIPT
}
</script>
...more stuff...
</body>
</html>
值得我们注意的有趣一点是,执行上面的例子,最后一个元素显示是 <script>
。实际上,<script>
下面还有内容呢,但是在脚本执行的这一刻,下面的节点内容还没有读到呢,也就是说 <script>
后面的内容对它是不可见的。
firstChild
和 lastChild
属性让我们能够快速访问某个元素第一个和最后一个孩子节点。
下例中,如果子节点存在,等式总是成立:
elem.childNodes[0] === elem.firstChild
elem.childNodes[elem.childNodes.length - 1] === elem.lastChild
还有一个 elem.hasChildNodes()
方法,用来判断某个元素下是否包含任何子节点。
DOM 集合
childNodes
看起来像个数组,但并不是数组,而是一个集合——一个特殊的是类数组可迭代对象。
有两个重要的结果:
- 我们可以用
for..of
去遍历这个集合:
for (let node of document.body.childNodes) {
alert(node); // 展示集合中的所有节点
}
因为这个集合是可迭代的(具有必要的 Symbol.iterator
属性)。
- 数组方法不能在集合中使用,因为它不是数组!
alert(document.body.childNodes.filter); // undefined (就没有 filter 方法!)
没有办法了吗?有,将集合转化成数组就可以了,用 ES6 提供的 Array.from
方法就能做到。
alert(Array.from(document.body.childNodes).filter);
DOM 集合是只读的
在本章中列举的所有遍历属性都是只读的。
我们不能用 childNodes[i] = ..`` 的方式创建一个节点。
修改 DOM 需要其他方法,会在下一章学习。
DOM 集合是实时的
除了少数例外,几乎所有DOM集合都是实时的。换句话说,它们反映了 DOM 的当前状态。
如果我们用一个变量引用了 childNodes``,并做了一些添加/删除操作,变化会在这个变量上“自动更新”。
不要在集合上用 **for..in`` 循环**
受到
for..of
可以遍历集合的诱导,有些人认为也可以用for..in
遍历集合。请不要!因为
for..in
循环本质上是遍历出对象上的所有可枚举属性,集合对象身上也存在少量很少使用的“其他”属性,会被我们遍历出来的。
<body>
<script>
// 打印出 0, 1, length, item, values 等等.
for (let prop in document.body.childNodes) alert(prop);
</script>
</body>
兄弟节点和父节点
兄弟节点是指拥有同一个父节点的节点。比如,<head>
和 <body>
就是兄弟节点:
<body>
可以说成在<head>
的“后面”或者“右面”,<head>
可以说成在<body>
的“前面”或者“左面”。
父节点使用 parentNode
属性获得。
某个节点的下一个节点,使用 nextSibling
获得;上一个节点使用 previousSibling
获得。
举个例子:
<html><head></head><body><script>
// 稠密排列的 HTML 文档保证规避额外的"空白"文本节点.
// <body> 的父节点是 <html>
alert( document.body.parentNode === document.documentElement ); // true
// <head> 的下一个节点是 <body>
alert( document.head.nextSibling ); // HTMLBodyElement
// <body> 的上一个节点是 <head>
alert( document.body.previousSibling ); // HTMLHeadElement
</script></body></html>
纯元素遍历
使用 childNodes
得到的子元素集合中,包含文本、元素,还有注释节点(有的话)。
但许多时候,我们不需要文本和注释节点,我们想要操作的是页面中的元素节点。
下面这张图片反应了 DOM 中,元素节点之间的遍历关系:
与 childNodes
类似,不过这里只考虑了元素节点的情况:
children
:所有的孩子节点(元素)。firstElementChild
、lastElementChild
:第一个、最后一个孩子节点(元素类型)。previousElementSibling
、nextElementSibling
:兄弟节点(元素类型)。parentElement
:父节点(元素类型)。
为什么有 parentElement,难道父级还能不是元素?
是的,可能。我们知道,parentElement
返回“元素”节点,而 parentNode
可返回“任何类型”的父节点。它们基本是一样的,但有一个区别,就是在 document.documentElement`` 上:
alert( document.documentElement.parentNode ); // document
alert( document.documentElement.parentElement ); // null
documentElement
(即 <html>
)就是根节点了,docuement是它的 parentNode
;但是 document不是一个元素节点,所以 documentElement
的 parentElement就是 null
啦。有时,我们在遍历父级链时,要针对每一个遍历的父级元素进行修改,并不想去碰 document
(因为 document
对象不具备元素节点方法),那么就可以选择使用 parentElement`` 。
更多链接:表格
到现在为止,我们已经讲解了基本的遍历属性。
某些特定 DOM 元素为了便捷,还提供了针对自身类型的额外属性。其中表格就是一个鲜活的例子。
`` 元素支持(除了上面提到的)还支持以下属性:
table.rows
:表格中所有<tr>
元素的集合。table.caption/tHead/tFoot
:分别指表格的<caption>
、<thead>
和<tFoot>
元素。table.tBodies
:<tbody>
元素的集合(根据标准,一个<table>
可以有多个<tbody>
)。
**、<tfoot>**
、` 元素也提供了
rows` 属性:tbody.rows
:当前<tbody>
中的<tr>
元素集合。
``:
tr.cells
:当前<tr>
中<td>
/<th>
单元格的集合。tr.sectionRowIndex
:当前<tr>
在<thead>
/<tbody>
/<tFoot>
中索引值。tr.rowIndex
: 当前<tr>
在整个表格中的索引值。
** 和 <th>** :
用法示例:
规范:tabular data。 HTML 表单元素也有其特定的遍历属性,这些知识点会在之后的章节中学习。 总结给定一个 DOM 节点,我们可以使用遍历属性访问它的邻居节点。 有两个主要的范围:
对于一些特定类型的 DOM 元素,比如表格,还提供了额外的便捷属性来访问它们的内容。 练习题问题一、DOM 的孩子元素节点有页面
怎么访问:
二、兄弟节点假设
三、选择对角线上的单元格将一个表格对角线上的单元格统一设置成红色背景,应该怎么做?
最终显示效果如下 答案一、
二、
需要注意的是,如果我们访问的 三、主要利用
(完) |