【问题标题】:Recursive HTML table tree using only JavaScript仅使用 JavaScript 的递归 HTML 表格树
【发布时间】:2018-06-11 16:02:27
【问题描述】:

我开发了单击父节点以显示其子行。我只需要启用单击子数据应该将其子子行打开为递归行或表树。任何人都可以添加您的逻辑来帮助我理解并帮助其他人吗?

document.getElementById("products").addEventListener("click", function(e) {
    if (e.target.tagName === "A") {
        e.preventDefault();
        var row = e.target.parentNode.parentNode;
        while ((row = nextTr(row)) && !/\bparent\b/ .test(row.className))
            toggle_it(row);
    }
});

function nextTr(row) {
    while ((row = row.nextSibling) && row.nodeType != 1);
    return row;
}

function toggle_it(item){ 
     if (/\bopen\b/.test(item.className))
         item.className = item.className.replace(/\bopen\b/," ");
     else
         item.className += " open";
 } 
tbody tr {
    display : none;
}
tr.parent {
    display : table-row;
}
tr.open {
    display : table-row;
}
<!-- 
  Bootstrap docs: https://getbootstrap.com/docs
-->

<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>
    <tr class="parent">
    <td><a href="#">+</a>Oranges</td>
        <td>100</td>
        <td>On Store</td>
        <td>22/10</td>
    </tr>
    <tr>
        <td>121</td>
        <td>120</td>
        <td>City 1</td>
        <td>22/10</td>
    </tr>
    <tr>
    <td>212</td>
        <td>140</td>
        <td>City 2</td>
        <td>22/10</td>
    </tr>
    <tr>
    <td>212</td>
        <td>140</td>
        <td>City 2</td>
        <td>22/10</td>
    </tr>
    <tr class="parent">
        <td><a href="#">+</a>Apples</td>
        <td>100</td>
        <td>On Store</td>
        <td>22/10</td>
    </tr>
    <tr>
        <td>120</td>
        <td>120</td>
        <td>City 1</td>
        <td>22/10</td>
    </tr>
    <tr>
        <td>120</td>
        <td>140</td>
        <td>City 2</td>
        <td>22/10</td>
    </tr>
    </tbody>
</table>
   
</div>

【问题讨论】:

    标签: javascript html


    【解决方案1】:

    更新答案

    我几乎更改了所有内容并简化了代码:

    • 自动添加切换按钮,
    • + 父级打开时变为-
    • 表格、打开和可见元素的类以及按钮作为参数传递,
    • 可以在多个表上使用,

    我在 GitHub 上使用该代码创建了一个存储库:
    https://github.com/TakitIsy/table-to-tree

    /* ---- < MAIN FUNCTION > ---- */
    function tableToTree(table_Selector, tr_OpenedClass, tr_VisibleClass, tr_ToggleButton) {
    
      // Table elements variables
      var table = document.querySelector(table_Selector);
      var trs = document.querySelectorAll(table_Selector + " tr");
    
      // Add the buttons above the table
      var buttons = document.createElement('div');
      buttons.innerHTML = '<button>[‒] All</button><button>[+] All</button>';
      table.insertBefore(buttons, table.childNodes[0]);
      buttons = buttons.querySelectorAll('button');
      // Add the actions of these buttons
      buttons[0].addEventListener("click", function() {
        trs.forEach(function(elm) {
          elm.classList.remove(tr_OpenedClass);
          elm.classList.remove(tr_VisibleClass);
        });
      });
      buttons[1].addEventListener("click", function() {
        trs.forEach(function(elm) {
          if (elm.innerHTML.includes(tr_ToggleButton))
            elm.classList.add(tr_OpenedClass);
          elm.classList.add(tr_VisibleClass);
        });
      });
    
      // Next tr function
      function nextTr(row) {
        while ((row = row.nextSibling) && row.nodeType != 1);
        return row;
      }
    
      // On creation, automatically add toggle buttons if the tr has childs elements
      trs.forEach(function(tr, index) {
        if (index < trs.length - 1) {
          if (+tr.getAttribute("level") < +trs[index + 1].getAttribute("level")) {
            var elm1 = tr.firstElementChild;
            elm1.innerHTML = tr_ToggleButton + elm1.innerHTML;
          }
        }
      });
    
      // Use the buttons added by the function above
      table.addEventListener("click", function(e) {
        
        // Event management
        if (!e) return;
        if (e.target.outerHTML !== tr_ToggleButton) return;
        e.preventDefault();
        
        // Get the parent tr and its level
        var row = e.target.closest("tr");
        row.classList.toggle(tr_OpenedClass);
        var lvl = +(row.getAttribute("level"));
    
        // Loop to make childs visible/hidden
        while ((row = nextTr(row)) && ((+(row.getAttribute("level")) == (lvl + 1)) || row.className.includes(tr_VisibleClass))) {
          row.classList.remove(tr_OpenedClass);
          row.classList.toggle(tr_VisibleClass);
        }
      });
    
    }
    /* ---- </ MAIN FUNCTION > ---- */
    
    // Call the above main function to make the table tree-like
    tableToTree('#myTable', 'opened', 'visible', '<span class="toggle"></span>');
    tbody tr {
      display: none;
    }
    
    tr[level="0"],
    tr.visible {
      display: table-row;
    }
    
    td {
      background: #ccc;
      padding: 4px 8px 4px 32px;
      text-align: left;
    }
    
    tr[level="1"] td {
      background: #ddd;
      padding-left: 40px;
    }
    
    tr[level="2"] td {
      background: #eee;
      padding-left: 48px;
    }
    
    tr .toggle {
      position: absolute;
      left: 16px;
      cursor: pointer;
    }
    
    .toggle::after {
      content: "[+]";
    }
    
    .opened .toggle::after {
      content: "[‒]";
    }
    <table id="myTable">
      <tbody>
        <tr level="0">
          <td>Parent 1</td>
        </tr>
        <tr level="1">
          <td>Match 1</td>
        </tr>
        <tr level="1">
          <td>Match 2</td>
        </tr>
        <tr level="0">
          <td>Parent 2</td>
        </tr>
        <tr level="1">
          <td>Mismatch 1</td>
        </tr>
        <tr level="1">
          <td>Mismatch 2</td>
        </tr>
        <tr level="2">
          <td>Mismatch 2.1</td>
        </tr>
      </tbody>
    </table>
    <br>

    ⋅ ⋅ ⋅

    旧答案

    我玩了一点你的代码……
    尝试尽可能多地使用现有的函数/方法,使代码更简洁,更易于阅读和理解。

    ……最后得到了那个 sn-p:
    (详情见我代码中的cmets)

    document.getElementById("products").addEventListener("click", function(e) {
      // I think using the not equal is nicer here, just my opinion… Less indenting.
      if (!e) return;                       // Exit if null
      if (e.target.tagName !== "A") return; // Exit if not A element
    
      e.preventDefault();
      var row = e.target.closest("tr"); // Better than parent parent!
      var cls = row.classList[0];       // Get the first class name (the only one in our html)
      var lvl = +(cls.slice(-1)) + 1;   // Unary operator +1 on the last character
      cls = cls.slice(0, -1) + lvl;     // Replace the last char with lvl to get the class to be toggled
    
      // Check if the row is of the class to be displayed OR if the row is already opened
      // (so that clicking close on parent also closes sub-childs)
      while ((row = nextTr(row)) && (row.className.includes(cls) || row.className.includes("open")))
        row.classList.toggle("open"); // Better than the function you created, it already exists!
    });
    
    function nextTr(row) {
      while ((row = row.nextSibling) && row.nodeType != 1);
      return row;
    }
    
    // Select all the tr childs elements (all levels except 0
    var allChilds = document.querySelectorAll("tr[class^=level]:not(.level0)");
    // Added the below for buttons after comments
    document.getElementById("openAll").addEventListener("click", function() {
      allChilds.forEach(function(elm){
        elm.classList.add("open");
      });
    });
    document.getElementById("closeAll").addEventListener("click", function() {
      allChilds.forEach(function(elm){
        elm.classList.remove("open");
      });
    });
    tbody tr {
      display: none;
    }
    
    
    /* Simplified */
    
    tr.level0,
    tr.open {
      display: table-row;
    }
    
    
    /* Added colors for better visibility */
    
    tr.level0 {
      background: #ccc;
    }
    
    tr.level1 {
      background: #ddd;
    }
    
    tr.level2 {
      background: #eee;
    }
    
    
    /* Added some more styling after comment */
    
    tr td {
      padding: 0.2em 0.4em;
    }
    
    tr td:first-of-type {
      position: relative;
      padding: 0.2em 1em;
    }
    
    tr td a {
      color: inherit;
      /* No special color */
      text-decoration: inherit;
      /* No underline */
      position: absolute;
      left: 0.25em;
    }
    
    tr.level1 td:first-of-type {
      padding-left: 1.5em;
    }
    
    tr.level2 td:first-of-type {
      padding-left: 2em;
    }
    <button id="openAll">+ All</button>
    <button id="closeAll">- All</button>
    <table class="table" id="products">
      <thead>
        <tr>
          <th>Product</th>
          <th>Price</th>
          <th>Destination</th>
          <th>Updated on</th>
        </tr>
      </thead>
      <tbody>
        <tr class="level0">
          <td><a href="#">+</a>Oranges</td>
          <td>100</td>
          <td>On Store</td>
          <td>22/10</td>
        </tr>
        <tr class="level1">
          <td>121</td>
          <td>120</td>
          <td>City 1</td>
          <td>22/10</td>
        </tr>
        <tr class="level1">
          <td><a href="#">+</a>212</td>
          <td>140</td>
          <td>City 2</td>
          <td>22/10</td>
        </tr>
        <tr class="level2">
          <td>212</td>
          <td>140</td>
          <td>City 2</td>
          <td>22/10</td>
        </tr>
        <tr class="level2">
          <td>212</td>
          <td>140</td>
          <td>City 2</td>
          <td>22/10</td>
        </tr>
        <tr class="level0">
          <td><a href="#">+</a>Apples</td>
          <td>100</td>
          <td>On Store</td>
          <td>22/10</td>
        </tr>
        <tr class="level1">
          <td>120</td>
          <td>120</td>
          <td>City 1</td>
          <td>22/10</td>
        </tr>
        <tr class="level1">
          <td><a href="#">+</a>120</td>
          <td>140</td>
          <td>City 2</td>
          <td>22/10</td>
          <tr class="level2">
            <td>120</td>
            <td>140</td>
            <td>City 2</td>
            <td>22/10</td>
          </tr>
          <tr class="level2">
            <td>120</td>
            <td>140</td>
            <td>City 2</td>
            <td>22/10</td>
          </tr>
      </tbody>
    </table>

    希望对你有帮助!

    【讨论】:

    • @njeoo 我已根据您的最后评论编辑了我的答案。我不只使用“一个”链接。我通常在 Google 中搜索我正在寻找的功能,例如,如果您输入“javascript add class”,您会找到几种不同的方法。通过越来越多地使用这些,您将了解在哪种情况下使用哪种方法,以及如何有效地使用它们。但无论如何,这里有一个链接,在那里你可以找到很多很多很多东西! developer.mozilla.org
    • 我试过这个只是改变了css3现在可以看到tr:我有tbody &gt; tr { display: table-row; } /* Simplified */ tr.level0, tr.open { display: table-row; } js的以下css如下:while((row = nextTr(row))&&( row.className.includes(cls) || row.className.includes("close"))) row.classList.toggle("open"); });
    • Takit Isy:请在 sn-ps 中进行更改,当我们单击父 tr 时,它应该显示带有 - 减号的子 tr,当我们再次单击子 tr 时,它应该关闭。
    • @njeoo 我建议你尝试使用新代码,我现在做了很多修改。 :)
    • 非常感谢....我会这样做的。是否可以将 json 数据作为父级和子级 tr 传递。当我们单击子级时,它应该首先显示父级,它应该显示子级td.你能不能创建json并传递数据。
    猜你喜欢
    • 1970-01-01
    • 2020-01-05
    • 1970-01-01
    • 1970-01-01
    • 2016-09-19
    • 1970-01-01
    • 1970-01-01
    • 2020-02-27
    • 2021-02-09
    相关资源
    最近更新 更多