ParentNode接口,ChildNode接口

本文详细介绍了DOM操作中的ParentNode和ChildNode接口,包括如何获取和操作元素子节点的方法,如children属性、firstElementChild、lastElementChild、childElementCount以及append、prepend等方法的使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原文地址:https://wangdoc.com/javascript/
节点对象除了继承Node接口以外,还会继承其他接口。ParentNode接口表示当前节点是一个父节点,提供一些处理子节点的方法。ChildNode接口表示当前节点是一个子节点,提供一些相关方法。

ParentNode接口

如果当前节点是父节点,就会继承ParentNode接口。由于只有元素节点(element)、文档节点(document)和文档片段节点(documentFragment)拥有子节点,因此只有这三类节点会继承ParentNode接口。

ParentNode.children

children属性返回一个HTMLCollection实例,成员是当前节点的所有元素子节点。该属性只读。
下面是遍历某个节点的所有元素子节点的示例。

for (var i = 0; i < el.children.length; i++) {
    // ...
}

注意,children属性只包含元素子节点,不包括其他类型的子节点(比如文本子节点)。如果没有元素类型的子节点,返回值HTMLCollection实例的length属性为0

ParentNode.firstElementChild

firstElementChild属性返回当前节点的第一个元素子节点。如果没有任何元素子节点,则返回null

document.firstElementChild.nodeName // HTML

ParentNode.lastElementChild

lastElementChild属性返回当前节点的最后一个元素子节点,如果不存在任何元素子节点,则返回null

document.lastElementChild.nodeName // HTML

上面代码中,document节点的最后一个元素子节点是(因为document只包含这一个元素子节点)。

ParentNode.childElementCount

childElementCount属性返回一个整数,表示当前节点的所有元素子节点的数目。如果不包含任何元素子节点,则返回0

document.body.childElementCount // 13

ParentNode.append(),ParentNode.prepend()

append方法为当前节点追加一个或多个子节点,位置是最后一个元素子节点的后面。
该方法不仅可以添加元素子节点,还可以添加文本子节点。

var parent = document.body;
// 添加元素子节点
var p = document.createElement("p");
parent.append(p);
// 添加文本子节点
parent.append("hello");

注意,该方法没有返回值。
prepend方法为当前节点追加一个或多个子节点,位置是第一个元素子节点的前面。它的用法与append方法完全一致,也是没有返回值。

ChildNode接口

如果一个节点有父节点,那么该节点就继承了ChildNode接口。

ChildNode.remove()

remove方法用于从父节点移除当前节点。

ChildNode.before(),ChildNode.after()

before方法用于在当前节点的前面,插入一个或多个同级节点。两者拥有相同的父节点。

var p = document.createElement("p");
var p1 = document.createElement("p");
// 插入元素节点
e1.before(p);
// 插入文本节点
e1.before("hello");
// 插入多个节点
e1.before(p, p1);

after方法用于在当前节点后面,插入一个或多个同级节点,两者拥有相同的父节点。用法与before方法完全相同。

ChildNode.replaceWith

replaceWith方法使用参数节点,替换当前节点。参数可以是元素节点,也可以是文本节点。

转载于:https://www.cnblogs.com/chris-jichen/p/10250032.html

问题出在 collectLevels 函数的逻辑,它没有正确按 ** 广度优先(层序)** 遍历节点,导致横向打印的层级数据 levels 顺序混乱,子节点 b 未被归入正确层级。#include <iostream> #include <vector> #include <string> #include <fstream> #include <queue> #include <algorithm> #include <map> using namespace std; class TreeNode { public: string name; vector<TreeNode*> children; TreeNode* parent; TreeNode(string n) : name(n), parent(nullptr) {} }; class FamilyTree { private: TreeNode* root; void printTreeHelper(TreeNode* node, int level) { for (int i = 0; i < level; i++) { cout << " "; } cout << node->name << endl; for (TreeNode* child : node->children) { printTreeHelper(child, level + 1); } } void collectLevels(TreeNode* node, int level, vector<vector<string>>& levels) { while (levels.size() <= (size_t)level) { levels.emplace_back(); } levels[level].push_back(node->name); for (TreeNode* child : node->children) { collectLevels(child, level + 1, levels); } } // 辅助函数:根据名称查找节点 TreeNode* findNodeByName(string name) { if (!root) return nullptr; queue<TreeNode*> q; q.push(root); while (!q.empty()) { TreeNode* node = q.front(); q.pop(); if (node->name == name) return node; for (auto* child : node->children) { q.push(child); } } return nullptr; } // 核心:动态计算节点位置并绘制横向结构(修复节点缺失问题) void printHorizontalStructure(const vector<vector<string>>& levels) { if (levels.empty()) return; // 建立真实父子映射(节点名称 -> 层级&索引) map<string, pair<int, int>> nodePos; for (size_t level = 0; level < levels.size(); ++level) { for (size_t i = 0; i < levels[level].size(); ++i) { nodePos[levels[level][i]] = {level, i}; } } // 计算每层节点的中心坐标 vector<vector<int>> nodeCenters; int totalWidth = 0; for (size_t level = 0; level < levels.size(); ++level) { vector<int> centers; int levelWidth = 0; for (const string& name : levels[level]) { levelWidth += name.size() + 4; } if (level == 0) { levelWidth = max(levelWidth, (int)levels[0][0].size() + 2); } int start = max(0, (totalWidth - levelWidth) / 2); for (const string& name : levels[level]) { int center = start + (int)name.size() / 2; centers.push_back(center); start += name.size() + 4; } nodeCenters.push_back(centers); totalWidth = max(totalWidth, levelWidth); } // 打印根节点及横线 int rootCenter = nodeCenters[0][0]; if (rootCenter < 0) rootCenter = 0; cout << string(rootCenter, ' ') << levels[0][0] << endl; int rootLineLen = levels[0][0].size(); int rootLineSpace = max(0, totalWidth - rootCenter - rootLineLen); cout << string(rootCenter, ' ') << string(rootLineLen, '-') << string(rootLineSpace, ' ') << endl; // 打印子节点层(基于真实父子映射) for (size_t level = 1; level < levels.size(); ++level) { const auto& currNodes = levels[level]; string line(totalWidth, ' '); // 遍历当前层每个节点,找真实父节点 for (size_t i = 0; i < currNodes.size(); ++i) { const string& childName = currNodes[i]; TreeNode* childNode = findNodeByName(childName); if (!childNode || !childNode->parent) continue; string parentName = childNode->parent->name; auto parentPosIt = nodePos.find(parentName); if (parentPosIt == nodePos.end()) continue; int parentLevel = parentPosIt->second.first; int parentIdx = parentPosIt->second.second; if (parentLevel != (int)level - 1) continue; int parentCenter = nodeCenters[parentLevel][parentIdx]; if (parentCenter < 0 || parentCenter >= (int)line.size()) { parentCenter = max(0, min((int)line.size() - 1, parentCenter)); } line[parentCenter] = '|'; int nodeStart = nodeCenters[level][i] - (int)childName.size() / 2; nodeStart = max(0, min(nodeStart, (int)line.size() - (int)childName.size())); for (size_t j = 0; j < childName.size(); ++j) { if (nodeStart + j < (int)line.size()) { line[nodeStart + j] = childName[j]; } } } cout << line << endl; // 打印下一层横线(若有) if (level + 1 < levels.size()) { string nextLine(totalWidth, ' '); for (size_t i = 0; i < currNodes.size(); ++i) { const string& childName = currNodes[i]; TreeNode* childNode = findNodeByName(childName); if (!childNode || !childNode->parent) continue; string parentName = childNode->parent->name; auto parentPosIt = nodePos.find(parentName); if (parentPosIt == nodePos.end()) continue; int parentLevel = parentPosIt->second.first; int parentIdx = parentPosIt->second.second; if (parentLevel != (int)level - 1) continue; int parentCenter = nodeCenters[parentLevel][parentIdx]; if (parentCenter >= 0 && parentCenter < (int)nextLine.size()) { nextLine[parentCenter] = '-'; } } cout << nextLine << endl; } } } void deleteNodeHelper(TreeNode* node) { for (TreeNode* child : node->children) { deleteNodeHelper(child); } delete node; } TreeNode* findNode(string parentName, string childName) { if (root == nullptr) return nullptr; if (root->name == parentName && childName.empty()) return root; vector<TreeNode*> queue; queue.push_back(root); while (!queue.empty()) { TreeNode* current = queue.front(); queue.erase(queue.begin()); for (TreeNode* child : current->children) { if (child->name == childName && current->name == parentName) { return child; } queue.push_back(child); } } return nullptr; } TreeNode* findParent(string parentName) { if (root == nullptr) return nullptr; if (root->name == parentName) return root; vector<TreeNode*> queue; queue.push_back(root); while (!queue.empty()) { TreeNode* current = queue.front(); queue.erase(queue.begin()); for (TreeNode* child : current->children) { if (child->name == parentName) { return child; } queue.push_back(child); } } return nullptr; } public: FamilyTree() : root(nullptr) {} ~FamilyTree() { if (root != nullptr) { deleteNodeHelper(root); } } bool addNode(string parentName, string childName) { if (parentName.empty() && childName.empty()) return false; if (root == nullptr) { if (!parentName.empty()) { cout << "根节点不能为空,请通过父节点输入 `,` 来创建根节点" << endl; return false; } root = new TreeNode(childName); return true; } if (parentName.empty()) { cout << "已有根节点,无法重复创建根节点" << endl; return false; } TreeNode* parentNode = findParent(parentName); if (parentNode == nullptr) { cout << "未找到父节点: " << parentName << ",添加失败" << endl; return false; } TreeNode* newNode = new TreeNode(childName); newNode->parent = parentNode; parentNode->children.push_back(newNode); return true; } bool deleteNode(string parentName, string childName) { TreeNode* nodeToDelete = findNode(parentName, childName); if (nodeToDelete == nullptr) { cout << "未找到节点: " << childName << "(父节点:" << parentName << ")" << endl; return false; } if (nodeToDelete == root) { deleteNodeHelper(root); root = nullptr; return true; } TreeNode* parent = nodeToDelete->parent; for (auto it = parent->children.begin(); it != parent->children.end(); ++it) { if (*it == nodeToDelete) { parent->children.erase(it); break; } } deleteNodeHelper(nodeToDelete); return true; } bool renameNode(string parentName, string oldName, string newName) { TreeNode* nodeToRename = findNode(parentName, oldName); if (nodeToRename == nullptr) { cout << "未找到节点: " << oldName << "(父节点:" << parentName << ")" << endl; return false; } nodeToRename->name = newName; return true; } void printTree() { if (root == nullptr) { cout << "家族树为空" << endl; return; } cout << "选择打印方式:" << endl; cout << "1. 传统树形结构(缩进式)" << endl; cout << "2. 横向分层结构(类似题目示例)" << endl; cout << "请输入选项(1/2): "; int choice; cin >> choice; cin.ignore(); if (choice == 1) { printTreeHelper(root, 0); } else if (choice == 2) { vector<vector<string>> levels; collectLevels(root, 0, levels); printHorizontalStructure(levels); } else { cout << "无效选项,默认展示传统树形结构" << endl; printTreeHelper(root, 0); } } bool saveToFile(string filename) { ofstream file(filename); if (!file.is_open()) { cout << "无法打开文件: " << filename << ",保存失败" << endl; return false; } vector<TreeNode*> queue; if (root != nullptr) { file << "," << root->name << endl; queue.push_back(root); while (!queue.empty()) { TreeNode* current = queue.front(); queue.erase(queue.begin()); for (TreeNode* child : current->children) { file << current->name << "," << child->name << endl; queue.push_back(child); } } } file.close(); return true; } bool loadFromFile(string filename) { ifstream file(filename); if (!file.is_open()) { cout << "无法打开文件: " << filename << ",加载失败" << endl; return false; } if (root != nullptr) { deleteNodeHelper(root); root = nullptr; } string line; while (getline(file, line)) { size_t pos = line.find(','); if (pos == string::npos) continue; string parentName = line.substr(0, pos); string childName = line.substr(pos + 1); addNode(parentName, childName); } file.close(); return true; } }; int main() { FamilyTree tree; string filename = "family_tree.txt"; while (true) { cout << "\n==== 家族树管理系统 ====" << endl; cout << "1. 添加节点(根节点父节点填 `,` ,输入 # 结束连续添加)" << endl; cout << "2. 删除节点" << endl; cout << "3. 重命名节点" << endl; cout << "4. 显示家族树" << endl; cout << "5. 保存到文件" << endl; cout << "6. 从文件加载" << endl; cout << "7. 退出系统" << endl; cout << "请输入操作序号: "; int choice; cin >> choice; cin.ignore(); switch (choice) { case 1: { cout << "\n=== 连续添加节点模式 ===" << endl; cout << "格式:父节点名称 子节点名称(根节点父节点填 `,` )" << endl; cout << "输入 # 结束添加" << endl; while (true) { string inputLine; cout << "请输入(父 子): "; getline(cin, inputLine); if (inputLine.empty()) continue; if (inputLine[0] == '#') break; size_t spacePos = inputLine.find(' '); if (spacePos == string::npos) { cout << "格式错误!需用空格分隔父节点和子节点" << endl; continue; } string parent = inputLine.substr(0, spacePos); string child = inputLine.substr(spacePos + 1); if (child.find('#') != string::npos) { cout << "子节点名称不能包含 # ,请重新输入" << endl; continue; } if (parent == ",") { parent = ""; } if (tree.addNode(parent, child)) { cout << "节点 [" << child << "] 添加成功(父节点:" << parent << ")" << endl; } else { cout << "节点 [" << child << "] 添加失败,请检查父节点是否存在" << endl; } } break; } case 2: { string parent, child; cout << "\n=== 删除节点 ===" << endl; cout << "请输入父节点名称: "; getline(cin, parent); cout << "请输入要删除的子节点名称: "; getline(cin, child); if (tree.deleteNode(parent, child)) { cout << "节点 [" << child << "] 删除成功" << endl; } else { cout << "删除失败,节点不存在或父节点不匹配" << endl; } break; } case 3: { string parent, oldName, newName; cout << "\n=== 重命名节点 ===" << endl; cout << "请输入父节点名称: "; getline(cin, parent); cout << "请输入原节点名称: "; getline(cin, oldName); cout << "请输入新节点名称: "; getline(cin, newName); if (tree.renameNode(parent, oldName, newName)) { cout << "节点 [" << oldName << "] 已重命名为 [" << newName << "]" << endl; } else { cout << "重命名失败,节点不存在或父节点不匹配" << endl; } break; } case 4: { cout << "\n=== 显示家族树 ===" << endl; tree.printTree(); break; } case 5: { cout << "\n=== 保存到文件 ===" << endl; if (tree.saveToFile(filename)) { cout << "家族树已保存至: " << filename << endl; } else { cout << "保存失败,请检查文件路径" << endl; } break; } case 6: { cout << "\n=== 从文件加载 ===" << endl; if (tree.loadFromFile(filename)) { cout << "家族树已从: " << filename << " 加载" << endl; } else { cout << "加载失败,请检查文件是否存在" << endl; } break; } case 7: { cout << "\n=== 退出系统 ===" << endl; cout << "感谢使用,再见!" << endl; return 0; } default: cout << "无效操作,请输入 1-7 之间的序号" << endl; break; } } return 0; }给出修改横向打印后的完整代码
最新发布
07-09
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值