递归HTML表格树只使用JavaScript [英] Recursive HTML table tree using only JavaScript

查看:58
本文介绍了递归HTML表格树只使用JavaScript的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



 <$ c $ e> document.getElementById(products)。addEventListener(click,function(e){if(e.target.tagName ===A){e.preventDefault(); var row = e.target (row = nextTr(row))&&!/ \ bparent\b / .test(row.className))toggle_it(row);}});函数nextTr(row).parentNode.parentNode; {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文档:https://getbootstrap.com/docs-->< ;; div class =container> < table class =tableid =products> < THEAD> < TR> <的第i;产品与LT; /第> <的第i;价格< /第> <的第i;目的地< /第> < th>更新于< / th> < / TR> < / THEAD> < TBODY> < tr class =parent> < td>< a href =#> +< / a>橘子< / td> < TD> 100℃; / TD> < td>存储< / td> < TD> 22/10 LT; / TD> < / TR> < TR> < TD> 121℃; / TD> < TD> 120℃; / TD> < td>城市1< / td> < TD> 22/10 LT; / TD> < / TR> < TR> < TD> 212< / TD> < TD> 140℃; / TD> < td>城市2< / td> < TD> 22/10 LT; / TD> < / TR> < TR> < TD> 212< / TD> < TD> 140℃; / TD> < td>城市2< / td> < TD> 22/10 LT; / TD> < / TR> < tr class =parent> < td>< a href =#> +< / a>苹果< / td> < TD> 100℃; / TD> < td>存储< / td> < TD> 22/10 LT; / TD> < / TR> < TR> < TD> 120℃; / TD> < TD> 120℃; / TD> < td>城市1< / td> < TD> 22/10 LT; / TD> < / TR> < TR> < TD> 120℃; / TD> < TD> 140℃; / TD> < td>城市2< / td> < TD> 22/10 LT; / TD> < / TR> < / tbody的>< /表> < / div>  

解决方案

我已经改变了几乎所有的东西,简化了代码:



ul>
  • 切换按钮会自动添加,当打开父项时,
  • + 会更改为 -

  • 表格,打开和可见元素的类以及按钮作为参数传递,它可以在多个表上使用,

  • ...



  • 我在GitHub上创建了一个具有该代码的存储库:

    https://github.com/TakitIsy/table-to-tree



    / * ----<主要功能> ---- * / function tableToTree(table_Selector,tr_OpenedClass,tr_VisibleClass,tr_ToggleButton){//表元素变量var table = document.querySelector(table_Selector); var trs = document.querySelectorAll(table_Selector +tr); //添加表格上方的按钮var buttons = document.createElement('div'); buttons.innerHTML ='<按钮> [ - ]全部< /按钮><按钮> [+]全部< /按钮>'; table.insertBefore(buttons,table.childNodes [0]); buttons = buttons.querySelectorAll('button'); //添加这些按钮的按钮[0] .addEventListener(click,function(){trs.forEach(function(elm){elm.classList.remove(tr_OpenedClass); elm.classList.remove(tr_VisibleClass); });});按钮[1] .addEventListener(click,function(){trs.forEach(function(elm){if(elm.innerHTML.includes(tr_ToggleButton))elm.classList.add(tr_OpenedClass); elm.classList.add tr_VisibleClass);});}); //下一个tr函数function nextTr(row){while((row = row.nextSibling)&& row.nodeType!= 1);返回行;如果tr具有childs元素,则自动添加切换按钮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;}}}); //使用由上面的函数添加的按钮.addEventListener(click,function(e){//事件管理if(!e)return; if(e.target.outerHTML!== tr_ToggleButton)return; e。 (); //获取父tr及其级别var row = e.target.closest(tr); row.classList.toggle(tr_OpenedClass); var lvl = +(row.getAttribute(level)) ; //循环使childs可见/隐藏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> ---- * ///调用上面的主函数来创建表格tree-liketableToTree('#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;左:16px; cursor:pointer;} toggle :: after {content:[+];}。打开.toggle :: after {content:[ - ];}  

    < table id =myTable>

     

    $ b < >





    旧回答



    我玩了一小段代码......

    尝试尽可能多地使用现有的函数/方法,以使代码更清晰,更易于阅读和理解。



    ...并以该片段结尾:

    (请参阅我的代码中的评论以获取更多详细信息)

    snippet-current-hidden ){//我认为在这里使用不等于更好,只是我的意见...减少缩进if(!e)return; //如果为空则退出if(e.target.tagName!==A)return; /如果不是,则返回一个元素e.preventDefault(); var row = e.targ et.closest( TR); //比父母更好! var cls = row.classList [0]; //获取第一个类名(我们html中唯一的名称)var lvl = +(cls.slice(-1))+ 1; //最后一个字符的一元运算符+1 cls = cls.slice(0,-1)+ lvl; //用lvl替换最后一个字符以获取要切换的类//检查该行是否为要显示的类,或者该行是否已经打开//(因此,单击关闭父项也会关闭子子项) while((row = nextTr(row))&& amp;(row.className.includes(cls)|| row.className.includes(open)))row.classList.toggle(open); //)函数nextTr(row){while((row = row.nextSibling)&& row.nodeType!= 1); //函数比你创建的函数更好,它已经存在! return row;} //选择所有tr childs元素(除0var外的所有级别allChilds = document.querySelectorAll(tr [class ^ = level]:not(.level0)); //在commentsdocument之后添加下面的按钮。 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;} / *简化* / tr.level0,tr.open {display:table -row;} * *添加了更好的可见性* / tr.level0 {background:#ccc;} tr.level1 {background:#ddd;} tr.level2 {background:#eee;} / *添加了更多样式评论* / tr td {padding:0.2em 0.4em;} tr td:first-of-type {position:relative; padding:0.2em 1em;} tr td a {color:inherit; / *没有特殊颜色* / text -decoration:inherit; / *无下划线* /位置:绝对; left:0.25em;} tr.level1 td:first-of-type {padding-left:1.5em;} tr.level2 td:first-of-type {padding-left:2em;} < code>< button id =openAll> +全部< / button><按钮

    id =closeAll> - 全部< / button>< table class =tableid =products> < THEAD> < TR> <的第i;产品与LT; /第> <的第i;价格< /第> <的第i;目的地< /第> < th>更新于< / th> < / TR> < / THEAD> < TBODY> < tr class =level0> < td>< a href =#> +< / a>橘子< / td> < TD> 100℃; / TD> < td>存储< / td> < TD> 22/10 LT; / TD> < / TR> < tr class =level1> < TD> 121℃; / TD> < TD> 120℃; / TD> < td>城市1< / td> < TD> 22/10 LT; / TD> < / TR> < tr class =level1> < td>< a href =#> +< / a> 212< / td> < TD> 140℃; / TD> < td>城市2< / td> < TD> 22/10 LT; / TD> < / TR> < tr class =level2> < TD> 212< / TD> < TD> 140℃; / TD> < td>城市2< / td> < TD> 22/10 LT; / TD> < / TR> < tr class =level2> < TD> 212< / TD> < TD> 140℃; / TD> < td>城市2< / td> < TD> 22/10 LT; / TD> < / TR> < tr class =level0> < td>< a href =#> +< / a>苹果< / td> < TD> 100℃; / TD> < td>存储< / td> < TD> 22/10 LT; / TD> < / TR> < tr class =level1> < TD> 120℃; / TD> < TD> 120℃; / TD> < td>城市1< / td> < TD> 22/10 LT; / TD> < / TR> < tr class =level1> < td>< a href =#> +< / a> 120< / td> < TD> 140℃; / TD> < td>城市2< / td> < TD> 22/10 LT; / TD> < tr class =level2> < TD> 120℃; / TD> < TD> 140℃; / TD> < td>城市2< / td> < TD> 22/10 LT; / TD> < / TR> < tr class =level2> < TD> 120℃; / TD> < TD> 140℃; / TD> < td>城市2< / td> < TD> 22/10 LT; / TD> < / TR> < / tbody>< / table>

    $ b

    帮助!


    I developed clicking on the parent node to display its child row. I just need to enable to click on the child data should open its sub child rows as recursive one or table tree. Could anyone add your logic which will help me to understand and help the same to others?

    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>

    解决方案

    Updated answer

    I've changed almost everything and simplified the code:

    • The toggle buttons are added automatically,
    • + changes to - when the parent is opened,
    • The table, the classes for opened and visible elements, and the buttons are passed as parameters,
    • It could be used on multiple table,

    I've created a repository with that code on 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>


    ⋅ ⋅ ⋅

    Old answer

    I played a little with your code…
    Trying to use as many as possible of existing functions/methods to make the code cleaner and easier to read and understand.

    … and ended-up with that snippet:
    (See comments in my code for more details)

    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>

    I hope it helps!

    这篇关于递归HTML表格树只使用JavaScript的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆