SlickGrid has this functionality, see the tree demo.
If you want to build your own, here is an example (jsFiddle demo): Build your table with a data-depth attribute to indicate the depth of the item in the tree (the levelX CSS classes are just for styling indentation):
<table id="mytable">
<tr data-depth="0" class="collapse level0">
<td><span class="toggle collapse"></span>Item 1</td>
<td>123</td>
</tr>
<tr data-depth="1" class="collapse level1">
<td><span class="toggle"></span>Item 2</td>
<td>123</td>
</tr>
</table>
Then when a toggle link is clicked, use Javascript to hide all <tr> elements until a <tr> of equal or less depth is found (excluding those already collapsed):
$(function() {
$('#mytable').on('click', '.toggle', function () {
//Gets all <tr>'s of greater depth below element in the table
var findChildren = function (tr) {
var depth = tr.data('depth');
return tr.nextUntil($('tr').filter(function () {
return $(this).data('depth') <= depth;
}));
};
var el = $(this);
var tr = el.closest('tr'); //Get <tr> parent of toggle button
var children = findChildren(tr);
//Remove already collapsed nodes from children so that we don't
//make them visible.
//(Confused? Remove this code and close Item 2, close Item 1
//then open Item 1 again, then you will understand)
var subnodes = children.filter('.expand');
subnodes.each(function () {
var subnode = $(this);
var subnodeChildren = findChildren(subnode);
children = children.not(subnodeChildren);
});
//Change icon and hide/show children
if (tr.hasClass('collapse')) {
tr.removeClass('collapse').addClass('expand');
children.hide();
} else {
tr.removeClass('expand').addClass('collapse');
children.show();
}
return children;
});
});
Answer from bcoughlan on Stack OverflowSlickGrid has this functionality, see the tree demo.
If you want to build your own, here is an example (jsFiddle demo): Build your table with a data-depth attribute to indicate the depth of the item in the tree (the levelX CSS classes are just for styling indentation):
<table id="mytable">
<tr data-depth="0" class="collapse level0">
<td><span class="toggle collapse"></span>Item 1</td>
<td>123</td>
</tr>
<tr data-depth="1" class="collapse level1">
<td><span class="toggle"></span>Item 2</td>
<td>123</td>
</tr>
</table>
Then when a toggle link is clicked, use Javascript to hide all <tr> elements until a <tr> of equal or less depth is found (excluding those already collapsed):
$(function() {
$('#mytable').on('click', '.toggle', function () {
//Gets all <tr>'s of greater depth below element in the table
var findChildren = function (tr) {
var depth = tr.data('depth');
return tr.nextUntil($('tr').filter(function () {
return $(this).data('depth') <= depth;
}));
};
var el = $(this);
var tr = el.closest('tr'); //Get <tr> parent of toggle button
var children = findChildren(tr);
//Remove already collapsed nodes from children so that we don't
//make them visible.
//(Confused? Remove this code and close Item 2, close Item 1
//then open Item 1 again, then you will understand)
var subnodes = children.filter('.expand');
subnodes.each(function () {
var subnode = $(this);
var subnodeChildren = findChildren(subnode);
children = children.not(subnodeChildren);
});
//Change icon and hide/show children
if (tr.hasClass('collapse')) {
tr.removeClass('collapse').addClass('expand');
children.hide();
} else {
tr.removeClass('expand').addClass('collapse');
children.show();
}
return children;
});
});
In modern browsers, you need only very little to code to create a collapsible tree :
var tree = document.querySelectorAll('ul.tree a:not(:last-child)');
for(var i = 0; i < tree.length; i++){
tree[i].addEventListener('click', function(e) {
var parent = e.target.parentElement;
var classList = parent.classList;
if(classList.contains("open")) {
classList.remove('open');
var opensubs = parent.querySelectorAll(':scope .open');
for(var i = 0; i < opensubs.length; i++){
opensubs[i].classList.remove('open');
}
} else {
classList.add('open');
}
e.preventDefault();
});
}
body {
font-family: Arial;
}
ul.tree li {
list-style-type: none;
position: relative;
}
ul.tree li ul {
display: none;
}
ul.tree li.open > ul {
display: block;
}
ul.tree li a {
color: black;
text-decoration: none;
}
ul.tree li a:before {
height: 1em;
padding:0 .1em;
font-size: .8em;
display: block;
position: absolute;
left: -1.3em;
top: .2em;
}
ul.tree li > a:not(:last-child):before {
content: '+';
}
ul.tree li.open > a:not(:last-child):before {
content: '-';
}
<ul class="tree">
<li><a href="#">Part 1</a>
<ul>
<li><a href="#">Item A</a>
<ul>
<li><a href="#">Sub-item 1</a></li>
<li><a href="#">Sub-item 2</a></li>
<li><a href="#">Sub-item 3</a></li>
</ul>
</li>
<li><a href="#">Item B</a>
<ul>
<li><a href="#">Sub-item 1</a></li>
<li><a href="#">Sub-item 2</a></li>
<li><a href="#">Sub-item 3</a></li>
</ul>
</li>
<li><a href="#">Item C</a>
<ul>
<li><a href="#">Sub-item 1</a></li>
<li><a href="#">Sub-item 2</a></li>
<li><a href="#">Sub-item 3</a></li>
</ul>
</li>
<li><a href="#">Item D</a>
<ul>
<li><a href="#">Sub-item 1</a></li>
<li><a href="#">Sub-item 2</a></li>
<li><a href="#">Sub-item 3</a></li>
</ul>
</li>
<li><a href="#">Item E</a>
<ul>
<li><a href="#">Sub-item 1</a></li>
<li><a href="#">Sub-item 2</a></li>
<li><a href="#">Sub-item 3</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="#">Part 2</a>
<ul>
<li><a href="#">Item A</a>
<ul>
<li><a href="#">Sub-item 1</a></li>
<li><a href="#">Sub-item 2</a></li>
<li><a href="#">Sub-item 3</a></li>
</ul>
</li>
<li><a href="#">Item B</a>
<ul>
<li><a href="#">Sub-item 1</a></li>
<li><a href="#">Sub-item 2</a></li>
<li><a href="#">Sub-item 3</a></li>
</ul>
</li>
<li><a href="#">Item C</a>
<ul>
<li><a href="#">Sub-item 1</a></li>
<li><a href="#">Sub-item 2</a></li>
<li><a href="#">Sub-item 3</a></li>
</ul>
</li>
<li><a href="#">Item D</a>
<ul>
<li><a href="#">Sub-item 1</a></li>
<li><a href="#">Sub-item 2</a></li>
<li><a href="#">Sub-item 3</a></li>
</ul>
</li>
<li><a href="#">Item E</a>
<ul>
<li><a href="#">Sub-item 1</a></li>
<li><a href="#">Sub-item 2</a></li>
<li><a href="#">Sub-item 3</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="#">Part 3</a>
<ul>
<li><a href="#">Item A</a>
<ul>
<li><a href="#">Sub-item 1</a></li>
<li><a href="#">Sub-item 2</a></li>
<li><a href="#">Sub-item 3</a></li>
</ul>
</li>
<li><a href="#">Item B</a>
<ul>
<li><a href="#">Sub-item 1</a></li>
<li><a href="#">Sub-item 2</a></li>
<li><a href="#">Sub-item 3</a></li>
</ul>
</li>
<li><a href="#">Item C</a>
<ul>
<li><a href="#">Sub-item 1</a></li>
<li><a href="#">Sub-item 2</a></li>
<li><a href="#">Sub-item 3</a></li>
</ul>
</li>
<li><a href="#">Item D</a>
<ul>
<li><a href="#">Sub-item 1</a></li>
<li><a href="#">Sub-item 2</a></li>
<li><a href="#">Sub-item 3</a></li>
</ul>
</li>
<li><a href="#">Item E</a>
<ul>
<li><a href="#">Sub-item 1</a></li>
<li><a href="#">Sub-item 2</a></li>
<li><a href="#">Sub-item 3</a></li>
</ul>
</li>
</ul>
</li>
</ul>
(see also this Fiddle)
I want to be able to render tree tables in Markdown documents. The sole method of achieving this appears to be by using HTML+ES6+CSS. That's too much for most processors. However, HTML is widely accepted.
I thought to request assistance at Stack Overflow. However, as you might have estimated, based upon me asking here, it was not well received.
Consequently, I didn't consider this to be possible, until I recently located https://gitlab.com/gitlab-org/gitlab/-/issues/21506#note_262038275, which appeared to demonstrate that it might be. Despite that, although I've been unable to reliably introduce nesting of more than one level using pure HTML:
<table> <thead> <tr> <th>1</th> <th>2</th> <th>3</th> </tr> </thead> <tbody> <tr> <td> <ol> <li> ph1 </td> <td>That</td> <td>Hip Hip</td> </tr> <tr> <td> </li> <ol> <li> ph1.ph1 </td> <td>That</td> <td>Hip Hip</td> </tr> <tr> <td> </li> <li> ph2.ph2 </td> <td>That</td> <td>Hip Hip</td> </tr> <tr> <td> </li> </ol> <li>ph3 <td> Attention </td> </li> </ol> </td> </tr> <tr> <td> <ol> <li> ph1 </td> <td>That</td> <td>Hip Hip</td> </tr> <tr> <td> </li> <ol> <li> ph1.ph1 </td> <td>That</td> <td>Hip Hip</td> </tr> <tr> <td> </li> <li> ph2.ph2 </td> <td>That</td> <td>Hip Hip</td> </tr> <tr> <td> </li> </ol> <li>ph3 <td> Attention </td> </li> </ol> </td> </tr> <tr> <td> <ol> <li> ph1 </td> <td>That</td> <td>Hip Hip</td> </tr> <tr> <td> </li> <ol> <li> ph1.ph1 </td> <td>That</td> <td>Hip Hip</td> </tr> <tr> <td> </li> <li> ph2.ph2 </td> <td>That</td> <td>Hip Hip</td> </tr> <tr> <td> </li> </ol> <li>ph3 <td> Attention </td> </li> </ol> </td> </tr> </tbody> </table>
Am I doing something incorrect, or might it be impossible?
| Seq | Item Name | Min | Max |
|---|---|---|---|
| 1 | Identifier | 1 | 1 |
| 2 | Name | 1 | 1 |
| 2.1 | First Name | 1 | 1 |
| 2.2 | Middle Name | - | - |
| 2.3 | Last Name | 1 | 1 |
| 3 | Age | - | 1 |
Use the semantically appropriate tag for lists: <ul>. simply nest them. you can hide part of the structure, or maybe create it on the fly.
<ul id='n0>
<li id='n1'>One guy</li>
<li id='n2'>Second guy
<ul id='n2.0'>
<li id='n2.1'>first one of second guy</li>
<li id='n2.2'>last of second</li>
</ul>
</li>
<li id='n3'>Third one</li>
</ul>
and so on. the naming of items is up to you, i usually do it either reflect the struture (as here), or the DB ids.
I don't have an answer, but I have an illustration for those who have trouble visualizing OP's need.
Unix QPS (visual process manager) in Tree View shows just such a tree/table.
Google image search finds a few sample images.
Personally, would love to know how to implement this in a browser.
Edit: Added a sample image:

(source: nada.kth.se)
Edit: Crude implementation
<!doctype html public "-//w3c//dtd xhtml 1.0 strict//en" "http://www.w3.org/tr/xhtml1/dtd/xhtml1-strict.dtd">
<html>
<head>
<style>
.removed
{
display:none;
}
.expands
{
cursor:pointer; cursor:hand;
}
.child1 td:first-child
{
padding-left: 1em;
}
.child2 td:first-child
{
padding-left: 2em;
}
</style>
<script>
function toggle()
{
for(var i=0; i<arguments.length; i++)
{
with(document.getElementById(arguments[i]))
{
if(className.indexOf('removed') > -1)
{
className = className.replace('removed');
}
else
{
className += ' removed';
}
}
}
}
</script>
</head>
<body>
<table>
<thead>
<tr>
<th>Person</th>
<th>Prop 1</th>
<th>Prop 2</th>
<th>Prop 3</th>
</tr>
</thead>
<tbody>
<tr id="p1" class="expands" onclick="toggle('p2','p3')">
<td>P1</td>
<td>a</td>
<td>b</td>
<td>c</td>
</tr>
<tr id="p2" class="removed child1">
<td>P2</td>
<td>a</td>
<td>b</td>
<td>c</td>
</tr>
<tr id="p3" class="removed child2">
<td>P3</td>
<td>a</td>
<td>b</td>
<td>c</td>
</tr>
</tbody>
<tfoot>
<tr>
<td>Totals:</td>
<td>x</td>
<td>y</td>
<td>z</td>
</tr>
</tfoot>
</table>
</body>
</html>