JavaScript常用DOM API研究详解06:parentNode、childNodes、firstChild、lastChild、nextSibling、previousSibling
在Web开发中,**DOM(文档对象模型)**是表示HTML或XML文档的编程接口。通过DOM,开发者可以访问和修改文档的结构、内容和样式。为了有效地操作DOM,理解节点之间的关系至关重要。
本篇文章将深入探讨以下六个常用的DOM节点属性:
parentNode
childNodes
firstChild
lastChild
nextSibling
previousSibling
通过详细的介绍和实例,将了解如何使用这些属性来遍历和操作DOM树。
一、DOM节点简介
1.1 什么是DOM节点?
在DOM中,文档的每个部分都是一个节点(Node)。节点类型包括:
- 元素节点:如
<div>
、<p>
等标签。 - 文本节点:元素或属性中的文本。
- 属性节点:元素的属性(在现代DOM中,属性作为元素的部分,通过
element.attributes
访问)。 - 注释节点:
<!-- 注释内容 -->
。
1.2 节点之间的关系
节点之间存在父子和兄弟关系,形成一棵树形结构,称为DOM树。理解这些关系可以帮助我们遍历和操作DOM。
二、parentNode
属性
2.1 什么是 parentNode
?
parentNode
是一个只读属性,返回当前节点的父节点。如果节点没有父节点(例如,是根节点),则返回 null
。
2.2 语法
const parent = node.parentNode;
node
:要获取父节点的节点。
2.3 示例
示例1:获取元素的父节点
<div id="parent">
<p id="child">这是一个段落。</p>
</div>
const child = document.getElementById('child');
const parent = child.parentNode;
console.log(parent.id); // 输出: "parent"
示例2:遍历父节点链
let node = document.getElementById('child');
while (node) {
console.log(node.nodeName);
node = node.parentNode;
}
输出:
P
DIV
BODY
HTML
#document
2.4 注意事项
parentNode
可以是任何节点类型:父节点可能是元素节点、文档节点等。- 文本节点的父节点:文本节点的父节点是包含它的元素。
三、childNodes
属性
3.1 什么是 childNodes
?
childNodes
是一个只读属性,返回一个 NodeList,包含了当前节点的所有子节点,包括元素节点、文本节点、注释节点等。
3.2 语法
const children = node.childNodes;
node
:要获取子节点的节点。
3.3 示例
示例1:获取元素的子节点
<ul id="list">
<li>项目 1</li>
<li>项目 2</li>
<li>项目 3</li>
</ul>
const list = document.getElementById('list');
const children = list.childNodes;
console.log(children.length); // 输出: 7
注意:为什么长度是7而不是3?因为childNodes
包含了所有类型的子节点,包括文本节点(如换行符、空白符)。
示例2:遍历子节点
children.forEach((node) => {
console.log(node.nodeName);
});
输出:
#text
LI
#text
LI
#text
LI
#text
3.4 过滤元素节点
如果只想获取元素子节点,可以使用 children
属性,或过滤 childNodes
。
使用 children
属性
const elementChildren = list.children;
console.log(elementChildren.length); // 输出: 3
过滤 childNodes
const elementNodes = Array.from(list.childNodes).filter(
(node) => node.nodeType === Node.ELEMENT_NODE
);
console.log(elementNodes.length); // 输出: 3
3.5 注意事项
childNodes
包含所有类型的子节点:文本节点、注释节点等。NodeList
不是数组:但可以使用Array.from()
转换为数组。- 实时更新:
childNodes
是实时的,DOM的变化会立即反映在其中。
四、firstChild
和 lastChild
属性
4.1 什么是 firstChild
和 lastChild
?
firstChild
:返回节点的第一个子节点,如果没有子节点,则返回null
。lastChild
:返回节点的最后一个子节点,如果没有子节点,则返回null
。
4.2 语法
const first = node.firstChild;
const last = node.lastChild;
4.3 示例
示例1:获取第一个和最后一个子节点
<div id="container">
<p>第一段</p>
<p>第二段</p>
<p>第三段</p>
</div>
const container = document.getElementById('container');
const firstChild = container.firstChild;
const lastChild = container.lastChild;
console.log(firstChild.nodeName); // 可能输出: "#text" 或 "P"
console.log(lastChild.nodeName); // 可能输出: "#text" 或 "P"
注意:如果元素内有换行或空白符,firstChild
可能是一个文本节点。
示例2:获取第一个和最后一个元素子节点
为了获取第一个和最后一个元素子节点,可以使用 firstElementChild
和 lastElementChild
。
const firstElementChild = container.firstElementChild;
const lastElementChild = container.lastElementChild;
console.log(firstElementChild.nodeName); // 输出: "P"
console.log(lastElementChild.nodeName); // 输出: "P"
4.4 注意事项
- 可能返回文本节点:如果节点的第一个或最后一个子节点是文本节点(如空白符、换行符),
firstChild
和lastChild
将返回文本节点。 - 使用
firstElementChild
和lastElementChild
:如果只想获取元素子节点,建议使用这两个属性。
五、nextSibling
和 previousSibling
属性
5.1 什么是 nextSibling
和 previousSibling
?
nextSibling
:返回当前节点在其父节点的childNodes
列表中的下一个节点,如果没有则返回null
。previousSibling
:返回当前节点在其父节点的childNodes
列表中的上一个节点,如果没有则返回null
。
5.2 语法
const next = node.nextSibling;
const previous = node.previousSibling;
5.3 示例
示例1:遍历兄弟节点
<ul id="list">
<li id="item1">项目 1</li>
<li id="item2">项目 2</li>
<li id="item3">项目 3</li>
</ul>
const item2 = document.getElementById('item2');
let previous = item2.previousSibling;
let next = item2.nextSibling;
console.log(previous.nodeName); // 可能输出: "#text" 或 "LI"
console.log(next.nodeName); // 可能输出: "#text" 或 "LI"
示例2:获取元素兄弟节点
为了获取元素兄弟节点,可以使用 nextElementSibling
和 previousElementSibling
。
const previousElement = item2.previousElementSibling;
const nextElement = item2.nextElementSibling;
console.log(previousElement.id); // 输出: "item1"
console.log(nextElement.id); // 输出: "item3"
5.4 注意事项
- 可能返回文本节点:如果兄弟节点是文本节点(如空白符、换行符),
nextSibling
和previousSibling
将返回文本节点。 - 使用
nextElementSibling
和previousElementSibling
:如果只想获取元素兄弟节点,建议使用这两个属性。 nextSibling
和previousSibling
可能为null
:如果当前节点是第一个或最后一个子节点,可能返回null
。
六、节点类型和 nodeType
属性
在使用上述属性时,可能会遇到文本节点和元素节点的区别。了解节点类型有助于更好地操作DOM。
6.1 常见的节点类型
- 元素节点:
nodeType === 1
,常量Node.ELEMENT_NODE
。 - 文本节点:
nodeType === 3
,常量Node.TEXT_NODE
。 - 注释节点:
nodeType === 8
,常量Node.COMMENT_NODE
。 - 文档节点:
nodeType === 9
,常量Node.DOCUMENT_NODE
。
6.2 示例:判断节点类型
function logNodeType(node) {
switch (node.nodeType) {
case Node.ELEMENT_NODE:
console.log('元素节点');
break;
case Node.TEXT_NODE:
console.log('文本节点');
break;
case Node.COMMENT_NODE:
console.log('注释节点');
break;
default:
console.log('其他类型节点');
}
}
const node = document.getElementById('item1');
logNodeType(node); // 输出: "元素节点"
七、实用技巧和实践示例
7.1 遍历元素子节点
由于 childNodes
包含所有类型的子节点,遍历时可能需要过滤出元素节点。
方法1:使用 children
属性
const parent = document.getElementById('list');
const children = parent.children;
Array.from(children).forEach((child) => {
console.log(child.nodeName);
});
方法2:使用 childNodes
并过滤
const children = parent.childNodes;
children.forEach((node) => {
if (node.nodeType === Node.ELEMENT_NODE) {
console.log(node.nodeName);
}
});
7.2 创建一个函数,递归遍历DOM树
function traverse(node, depth = 0) {
const indent = ' '.repeat(depth);
console.log(`${indent}${node.nodeName}`);
node = node.firstChild;
while (node) {
traverse(node, depth + 1);
node = node.nextSibling;
}
}
traverse(document.body);
7.3 获取元素的所有祖先节点
function getAncestors(node) {
const ancestors = [];
while (node.parentNode) {
node = node.parentNode;
if (node.nodeType === Node.ELEMENT_NODE) {
ancestors.push(node);
}
}
return ancestors;
}
const item = document.getElementById('item1');
const ancestors = getAncestors(item);
ancestors.forEach((ancestor) => {
console.log(ancestor.nodeName);
});
八、总结
在本文中,我们深入探讨了以下DOM节点属性:
parentNode
:获取当前节点的父节点。childNodes
:获取当前节点的所有子节点,包括文本节点和注释节点。firstChild
和lastChild
:获取第一个和最后一个子节点,可能是文本节点。nextSibling
和previousSibling
:获取当前节点的下一个和上一个兄弟节点,可能是文本节点。
为了避免获取到文本节点,我们可以使用以下属性:
children
:获取所有元素子节点。firstElementChild
和lastElementChild
:获取第一个和最后一个元素子节点。nextElementSibling
和previousElementSibling
:获取下一个和上一个元素兄弟节点。
理解这些属性和节点之间的关系,有助于我们更有效地遍历和操作DOM树。
参考资料
- MDN Web Docs - Node.parentNode
- MDN Web Docs - Node.childNodes
- MDN Web Docs - Node.firstChild
- MDN Web Docs - Node.lastChild
- MDN Web Docs - Node.nextSibling
- MDN Web Docs - Node.previousSibling
- MDN Web Docs - Element.children
- JavaScript高级程序设计(第4版)