How to create a collapsing tree table in html/css/js?
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;
});
});
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)
jquery is your friend here.
http://docs.jquery.com/UI/Tree
If you want to make your own, here is some high level guidance:
Display all of your data as <ul />
elements with the inner data as nested <ul />
, and then use the jquery:
$('.ulClass').click(function(){ $(this).children().toggle(); });
I believe that is correct. Something like that.
EDIT:
Here is a complete example.
$(".Collapsable").click(function () {
$(this).parent().children().toggle();
$(this).toggle();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul>
<li><span class="Collapsable">item 1</span><ul>
<li><span class="Collapsable">item 1</span></li>
<li><span class="Collapsable">item 2</span><ul>
<li><span class="Collapsable">item 1</span></li>
<li><span class="Collapsable">item 2</span></li>
<li><span class="Collapsable">item 3</span></li>
<li><span class="Collapsable">item 4</span></li>
</ul>
</li>
<li><span class="Collapsable">item 3</span></li>
<li><span class="Collapsable">item 4</span><ul>
<li><span class="Collapsable">item 1</span></li>
<li><span class="Collapsable">item 2</span></li>
<li><span class="Collapsable">item 3</span></li>
<li><span class="Collapsable">item 4</span></li>
</ul>
</li>
</ul>
</li>
<li><span class="Collapsable">item 2</span><ul>
<li><span class="Collapsable">item 1</span></li>
<li><span class="Collapsable">item 2</span></li>
<li><span class="Collapsable">item 3</span></li>
<li><span class="Collapsable">item 4</span></li>
</ul>
</li>
<li><span class="Collapsable">item 3</span><ul>
<li><span class="Collapsable">item 1</span></li>
<li><span class="Collapsable">item 2</span></li>
<li><span class="Collapsable">item 3</span></li>
<li><span class="Collapsable">item 4</span></li>
</ul>
</li>
<li><span class="Collapsable">item 4</span></li>
</ul>
I'll throw jsTree into the ring, too. I've found it fairly adaptable to your particular situation. It's packed as a jQuery plugin.
It can run from a variety of data sources, but my favorite is a simple nested list, as described by @joe_coolish or here:
<ul>
<li>
Item 1
<ul>
<li>Item 1.1</li>
...
</ul>
</li>
...
</ul>
This structure fails gracefully into a static tree when JS is not available in the client, and is easy enough to read and understand from a coding perspective.
HTML 5 allows summary tag, details element. That can be used to view or hide (collapse/expand) a section. Link