You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于JavaScript实现递归HTML表格树:子节点展开功能需求

好的,咱们来把这个表格改成支持递归展开的树形结构!先分析下你现有代码的局限:原来的逻辑只处理了顶级父节点(带parent类的行),点击后展开后面的行直到下一个parent,但没法处理子节点本身还有下一级子行的情况。要实现递归展开,咱们得调整标记、事件逻辑和样式这几个部分。


核心实现逻辑

  • 统一标记可展开节点:给所有包含子节点的行(不管是顶级父节点还是子级父节点)添加通用类has-children,同时用data-level属性记录节点层级(比如顶级是0,子级是1,孙子级是2)——既方便识别可点击节点,也能做样式缩进区分层级。
  • 精准匹配子行范围:点击某个has-children行时,只处理它后面层级比当前行高1级的行,直到遇到层级小于等于当前行的行为止,这样就能准确圈出当前节点的直接子节点,不会误操作其他层级的内容。
  • 递归控制显示状态:切换子行显示时,子行里的has-children节点默认保持折叠,用户点击时再展开它的下一级,形成递归展开的效果;同时折叠父节点时,要递归隐藏所有下级子行。
  • 视觉层级优化:根据data-level给不同层级的行添加左侧缩进,让树形结构更直观。

修改后的完整代码

HTML(带层级标记)

<div class="container">
  <table class="table" id="products">
    <thead>
      <tr>
        <th>Product</th>
        <th>Price</th>
        <th>Destination</th>
        <th>Updated on</th>
      </tr>
    </thead>
    <tbody>
      <!-- 顶级节点,层级0 -->
      <tr class="has-children" data-level="0">
        <td><a href="#"></a>Oranges</td>
        <td>100</td>
        <td>On Store</td>
        <td>22/10</td>
      </tr>
      <!-- 子节点,层级1,自身带下级 -->
      <tr class="has-children" data-level="1">
        <td><a href="#"></a>121</td>
        <td>120</td>
        <td>City 1</td>
        <td>22/10</td>
      </tr>
      <!-- 孙子节点,层级2 -->
      <tr data-level="2">
        <td>121-1</td>
        <td>130</td>
        <td>Suburb 1</td>
        <td>22/10</td>
      </tr>
      <tr data-level="2">
        <td>121-2</td>
        <td>135</td>
        <td>Suburb 2</td>
        <td>22/10</td>
      </tr>
      <tr data-level="1">
        <td>212</td>
        <td>140</td>
        <td>City 2</td>
        <td>22/10</td>
      </tr>
      <!-- 另一个顶级节点 -->
      <tr class="has-children" data-level="0">
        <td><a href="#"></a>Apples</td>
        <td>100</td>
        <td>On Store</td>
        <td>22/10</td>
      </tr>
      <tr data-level="1">
        <td>120</td>
        <td>120</td>
        <td>City 1</td>
        <td>22/10</td>
      </tr>
    </tbody>
  </table>
</div>

CSS(层级缩进+显示控制)

/* 默认隐藏所有行 */
tbody tr {
  display: none;
}
/* 默认显示顶级节点(层级0) */
tbody tr[data-level="0"] {
  display: table-row;
}
/* 展开状态的行显示 */
tbody tr.open {
  display: table-row;
}
/* 不同层级的缩进样式,视觉区分 */
tbody tr[data-level="1"] td:first-child {
  padding-left: 20px;
}
tbody tr[data-level="2"] td:first-child {
  padding-left: 40px;
}
/* 可展开节点的箭头切换样式 */
.has-children a {
  text-decoration: none;
  margin-right: 8px;
}
.has-children.open a::before {
  content: "-";
}
.has-children:not(.open) a::before {
  content: "+";
}

JavaScript(递归展开逻辑)

document.getElementById("products").addEventListener("click", function(e) {
  if (e.target.tagName === "A") {
    e.preventDefault();
    const currentRow = e.target.closest("tr");
    const currentLevel = parseInt(currentRow.dataset.level);
    // 切换当前节点的展开状态
    currentRow.classList.toggle("open");

    // 遍历当前行之后的所有行,处理直接子节点
    let nextRow = currentRow.nextElementSibling;
    while (nextRow) {
      const nextLevel = parseInt(nextRow.dataset.level);
      if (nextLevel === currentLevel + 1) {
        // 父节点展开时显示子行,折叠时隐藏并递归折叠子行的下级
        if (currentRow.classList.contains("open")) {
          nextRow.style.display = "table-row";
          // 子节点默认保持折叠状态
          nextRow.classList.remove("open");
        } else {
          nextRow.style.display = "none";
          hideChildRows(nextRow, nextLevel);
        }
      } else if (nextLevel <= currentLevel) {
        // 遇到同层级或更高层级的节点,停止遍历
        break;
      }
      nextRow = nextRow.nextElementSibling;
    }
  }
});

// 递归隐藏子行的所有下级节点
function hideChildRows(row, parentLevel) {
  let nextRow = row.nextElementSibling;
  while (nextRow) {
    const nextLevel = parseInt(nextRow.dataset.level);
    if (nextLevel === parentLevel + 1) {
      nextRow.style.display = "none";
      nextRow.classList.remove("open");
      // 继续递归隐藏下级
      hideChildRows(nextRow, nextLevel);
    } else if (nextLevel <= parentLevel) {
      break;
    }
    nextRow = nextRow.nextElementSibling;
  }
}

关键逻辑说明

  1. 层级标记:通过data-level明确节点的嵌套关系,让JS能精准识别当前节点的子范围,同时CSS可以基于层级做视觉区分。
  2. 子行范围判断:只处理层级为当前层级+1的行,避免误操作其他层级的内容,确保递归展开的准确性。
  3. 递归隐藏:折叠父节点时,调用hideChildRows函数递归隐藏所有下级子行,避免出现父节点折叠但子节点下级还显示的混乱情况。

内容的提问来源于stack exchange,提问作者njeoo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 09:41:12