簡體   English   中英

僅使用JavaScript的遞歸HTML表樹

[英]Recursive HTML table tree using only JavaScript

我開發了單擊父節點以顯示其子行的方法。 我只需要啟用就可以單擊子數據,應將其子子行作為遞歸樹或表樹打開。 任何人都可以添加您的邏輯,這將有助於我理解並幫助他人。

 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> 

更新的答案

我幾乎更改了所有內容並簡化了代碼:

  • 切換按鈕會自動添加,
  • +更改為-打開父級后,
  • 該表,打開的元素和可見元素的類以及按鈕均作為參數傳遞,
  • 它可以在多個表上使用,
  • ...

我已經在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> 


⋅⋅⋅

舊答案

我用您的代碼玩了一點……
嘗試使用盡可能多的現有功能/方法,以使代碼更整潔,更易於閱讀和理解。

…最后是那個片段:
(有關更多詳細信息,請參見我的代碼中的注釋)

 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> 

希望對您有所幫助!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM