JavaScript常用DOM API研究详解06:parentNode、childNodes、firstChild、lastChild、nextSibling、previousSibling

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的变化会立即反映在其中。

四、firstChildlastChild 属性

4.1 什么是 firstChildlastChild

  • 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:获取第一个和最后一个元素子节点

为了获取第一个和最后一个元素子节点,可以使用 firstElementChildlastElementChild

const firstElementChild = container.firstElementChild;
const lastElementChild = container.lastElementChild;

console.log(firstElementChild.nodeName); // 输出: "P"
console.log(lastElementChild.nodeName);  // 输出: "P"

4.4 注意事项

  • 可能返回文本节点:如果节点的第一个或最后一个子节点是文本节点(如空白符、换行符),firstChildlastChild 将返回文本节点。
  • 使用 firstElementChildlastElementChild:如果只想获取元素子节点,建议使用这两个属性。

五、nextSiblingpreviousSibling 属性

5.1 什么是 nextSiblingpreviousSibling

  • 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:获取元素兄弟节点

为了获取元素兄弟节点,可以使用 nextElementSiblingpreviousElementSibling

const previousElement = item2.previousElementSibling;
const nextElement = item2.nextElementSibling;

console.log(previousElement.id); // 输出: "item1"
console.log(nextElement.id);     // 输出: "item3"

5.4 注意事项

  • 可能返回文本节点:如果兄弟节点是文本节点(如空白符、换行符),nextSiblingpreviousSibling 将返回文本节点。
  • 使用 nextElementSiblingpreviousElementSibling:如果只想获取元素兄弟节点,建议使用这两个属性。
  • nextSiblingpreviousSibling 可能为 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:获取当前节点的所有子节点,包括文本节点和注释节点。
  • firstChildlastChild:获取第一个和最后一个子节点,可能是文本节点。
  • nextSiblingpreviousSibling:获取当前节点的下一个和上一个兄弟节点,可能是文本节点。

为了避免获取到文本节点,我们可以使用以下属性:

  • children:获取所有元素子节点。
  • firstElementChildlastElementChild:获取第一个和最后一个元素子节点。
  • nextElementSiblingpreviousElementSibling:获取下一个和上一个元素兄弟节点。

理解这些属性和节点之间的关系,有助于我们更有效地遍历和操作DOM树。


参考资料


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

It'sMyGo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值