JavaScript 教程:使用 getElementsByTagName 统计嵌套 ul/li 树中每个节点的后代数量

发布时间:2026/10/6 12:23:05
JavaScript 教程:使用 getElementsByTagName 统计嵌套 ul/li 树中每个节点的后代数量 文档/教程前端【免费下载链接】en.javascript.infoModern JavaScript Tutorial项目地址https://gitcode.com/gh_mirrors/en/en.javascript.info点击查看免费下载本篇文章基于 Modern JavaScript Tutorialen.javascript.info的「DOM 修改」章节中一个高价值实战任务8-tree-count完整还原问题场景与官方解法给定一棵由嵌套ul/li构成的树编写代码在每个li末尾追加其后代li的数量叶子节点跳过。读完本文你将掌握getElementsByTagName的集合语义、firstChild.data直接修改文本节点的技巧以及如何用最简单的 DOM API 完成树形统计。任务目标为树的每个节点标注后代数量任务要求十分明确页面中存在一棵由嵌套的ul/li组织起来的树source.view/index.html为每个li追加文本内容为它的后代li的数量跳过叶子节点即没有子li的节点。期望效果如下solution.view/index.html 展示了运行结果Animals含Mammals、Other两个分支及其全部子项Mammals含 4 个子项Cows、Donkeys、Dogs、TigersFishes含Aquarium、Sea两个分支……以此类推最底层的Cows、Donkeys等叶子节点不追加任何内容。初始 HTML 结构source.view/index.html 给出了任务的初始骨架树形结构如下已省略重复的Sea分支细节!DOCTYPE HTML html body ul liAnimals ul liMammals ul liCows/li liDonkeys/li liDogs/li liTigers/li /ul /li liOther ul liSnakes/li liBirds/li liLizards/li /ul /li /ul /li liFishes ul liAquarium ul liGuppy/li liAngelfish/li /ul /li liSea ul liSea trout/li /ul /li /ul /li /ul script // ... your code ... /script /body /html注意这里的一个结构细节每个非叶子li的第一个子节点是文本节点如Animals、Mammals随后才是子ul。这个文本节点的存在正是解法能直接“原地追加文本”的关键。官方解法两行核心逻辑官方解法 给出的提示是“要往每个li追加文本我们可以直接修改文本节点data”。完整实现位于 solution.view/index.htmllet lis document.getElementsByTagName(li); for (let li of lis) { // 统计当前 li 下方的所有 li let descendantsCount li.getElementsByTagName(li).length; if (!descendantsCount) continue; // 直接追加到文本节点在文本后面拼上 [数量] li.firstChild.data [ descendantsCount ]; }运行后页面呈现为Animals [5]其下共有 5 个liMammals [4]Cows、Donkeys、Dogs、TigersOther [3]Snakes、Birds、LizardsFishes [4]其下共有 4 个liAquarium [2]Guppy、AngelfishSea [1]Sea trout最底层的Cows、Donkeys等叶子节点原样不动。关键 API 拆解1.document.getElementsByTagName(li)整棵树的所有ligetElementsByTagName是 DOM 搜索 API 中的经典方法在教程的「搜索元素」章节中有明确说明elem.getElementsByTagName(tag)返回给定标签的元素集合tag参数还可以传*表示“任意标签”方法名中带有复数形式的Elements因为返回的是一个集合HTMLCollection这与返回单个元素的getElementById形成对照——这也是教程特别提醒过的易错点getElementById没有字母s而getElementsByTagName有集合中的元素可以通过索引访问例如document.getElementsByTagName(input)[0]也可以通过for...of直接迭代。在第一行代码中document.getElementsByTagName(li)收集的是文档范围内全部li因此for...of循环会覆盖外层节点和所有深层嵌套节点。2.li.getElementsByTagName(li)子树内的后代li解法最关键的一步是在元素实例上再次调用getElementsByTagName(li)它不再搜索整个文档而是只搜索以当前li为根的子树返回其中所有的后代li不包含当前节点自身。因此descendantsCount恰好就是“当前节点的后代数量”。由于嵌套的li只会以“祖先后代”的方式存在树中不存在兄弟li互相包含的情况这里无需递归、无需去重——每个后代li都唯一地归属于某个祖先分支数量统计天然正确。3.li.firstChild.data [...]直接操作文本节点这是官方解法强调的“锦上添花”技巧。在 DOM 中liAnimalsul.../ul/li这样的写法其子节点依次为文本节点Animals即li.firstChild元素节点ul即li.lastChild。文本节点的内容存储在.data属性中。所以li.firstChild.data [5]等价于“把 [5]追加到文本末尾”不会破坏原有文本、也不会误删子ul。整段操作没有新建任何元素、没有插入节点仅仅是修改既有文本节点因此即使getElementsByTagName返回的是实时live集合循环遍历的集合结构也不会因修改而发生变化。// 以 liAnimals .../li 为例 li.firstChild // 文本节点 Animals li.firstChild.data // Animals li.firstChild.data [5]; // 变为 Animals [5]叶子节点为何被跳过if (!descendantsCount) continue;叶子节点如Cows、Guppy内部没有任何lili.getElementsByTagName(li).length返回0条件!0为真直接continue跳过。这一行同时承担了两件事既过滤了叶子节点又保证了文本格式的统一只有有后代的节点才追加[N]。完整可运行代码将以下脚本放入 source.view/index.html 的script标签中即可在浏览器中直接验证最终效果见 solution.view/index.htmlscript let lis document.getElementsByTagName(li); for (let li of lis) { let descendantsCount li.getElementsByTagName(li).length; if (!descendantsCount) continue; li.firstChild.data [ descendantsCount ]; } /script解法背后的 DOM 修改知识体系本任务出自教程的「修改文档Modifying the document」章节该章节系统讲解了 DOM 的创建、插入、删除与克隆方法与本任务相关的核心知识点包括节点创建document.createElement(tag)创建元素节点、document.createTextNode(text)创建文本节点本任务没有新建节点走的是“改”而非“建”的路线插入方法node.append(...)、node.prepend(...)、node.before(...)、node.after(...)、node.replaceWith(...)——如果任务要求的是“追加一个带计数的子元素”而非“在文本后拼接”这些方法就是首选文本插入安全性append等方法插入字符串时按纯文本处理自动转义、不会解析为 HTML如需按 HTML 解析则用elem.insertAdjacentHTML(where, html)节点删除node.remove()。理解这些背景有助于判断本题选择直接修改firstChild.data是因为只需要改文本、不涉及结构变更这是最高效、最不容易出错的做法而一旦需要新增元素节点例如在每个li里再插入一个计数徽章则应改用createElementappend的组合。常见实现误区误用innerHTML整体重写如li.innerHTML li.innerHTML [5]会把子ul一并覆盖或导致结构损坏且会触发不必要的重解析务必避免统计范围错误只对顶层li调用getElementsByTagName(li)无法得到每个节点的独立计数必须在每一个li自身上调用才能获得其子树范围未跳过叶子节点漏写if (!descendantsCount) continue;会导致所有叶子li被追加成[0]与题目要求不符误改元素节点firstChild取到的是文本节点才能用.data若某个li以子元素开头如lispan.../span/li则需要改用li.firstElementChild或先定位到对应文本节点。小结知识点本任务中的应用document.getElementsByTagName(li)获取文档内全部li用于外层遍历li.getElementsByTagName(li)获取当前节点子树内的后代li用于计数length属性集合元素个数即后代数量为0说明是叶子节点firstChild.data ...向既有文本节点追加文本保持结构不变核心代码仅 4 行却完整覆盖了“集合搜索 子树统计 文本节点修改 条件跳过”四个要点是理解 DOM 搜索 API 与节点修改的最佳入门实战。完整题目与解法可在仓库中直接查看任务描述、官方解法说明、可运行示例。赞分享文档/教程前端【免费下载链接】en.javascript.infoModern JavaScript Tutorial项目地址https://gitcode.com/gh_mirrors/en/en.javascript.info点击查看免费下载相关推荐现代 JavaScript 教程遍历嵌套 ul/li 树统计每个节点的后代数量现代 JavaScript 教程遍历嵌套 ul/li 树统计每个节点的后代数量 导读 本文围绕《Modern JavaScript Tutorial》仓库中文档/教程前端Modern JavaScript Tutorial 实战遍历嵌套 ul/li 树统计每个节点的文本标题与后代数量Modern JavaScript Tutorial 实战遍历嵌套 ul/li 树统计每个节点的文本标题与后代数量 本文围绕 en.javascript.i文档/教程前端用 JavaScript 从嵌套对象递归构建 ul/li 树形列表innerHTML 与 DOM 双方案实战用 JavaScript 从嵌套对象递归构建 ul/li 树形列表innerHTML 与 DOM 双方案实战 导读 在《现代 JavaScript 教程》的文档/教程前端上一篇163MusicLyrics免费开源歌词提取工具终极指南轻松获取网易云与QQ音乐歌词下一篇Umi-OCR开启你的离线文字识别新纪元创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考