Last active
February 14, 2020 16:17
-
-
Save ianchanning/7412377 to your computer and use it in GitHub Desktop.
Pure JavaScript hierarchical table row toggling (plus older jQuery version too)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Hierarchical table rows object | |
* | |
* Pure JavaScript - but comes with a IE9+ warning | |
* | |
* @param {object} options tableSelector e.g. 'table' | |
* parentClass e.g. 'parent' | |
* collapsedClass e.g. 'hidden' | |
* childClassPrefix e.g. 'parentId' | |
* @type function | |
* @return object | |
* @licence MIT | |
*/ | |
var treeTable = function (options) { | |
'use strict'; | |
var tableSelector = (options && options.tableSelector) || 'table'; | |
var parentClass = (options && options.parentClass) || 'header'; | |
var childClassPrefix = (options && options.childClassPrefix) || ''; | |
var collapsedClass = (options && options.collapsedClass) || 'collapsed'; | |
/** | |
* Recursively hide all child rows or show immediate children | |
* | |
* All direct children must have a class that is the same as the parent id | |
* with an optional prefix | |
* | |
* @param {object} parentRow row element object | |
*/ | |
var toggleRowChildren = function(parentRow) { | |
/** | |
* Replicate jQuery toggle | |
* | |
* @param {object} row element object | |
*/ | |
var toggle = function(row) { | |
row.style.display = row.style.display ? '' : 'none'; | |
return row; | |
}; | |
/** | |
* Encapsulate the recursion check | |
* | |
* @param {object} row element object | |
* @return {boolean} if the row is collapsible | |
*/ | |
var collapsible = function(row) { | |
return row.classList.contains(parentClass) && | |
!row.classList.contains(collapsedClass); | |
}; | |
var childClass = childClassPrefix + parentRow.getAttribute('id'); | |
var childrenRows = parentRow.parentNode.querySelectorAll('tr.'+childClass); | |
// toggle all the children | |
childrenRows.forEach(function(row){ | |
toggle(row); | |
// if a child is a parent and isn't collapsed | |
if (collapsible(row)) { | |
// recurse to the child | |
toggleRowChildren(row); | |
} | |
}); | |
// 'mark' that the child has been hidden or not | |
parentRow.classList.toggle(collapsedClass); | |
}; | |
return { | |
init : function() { | |
/** | |
* event delegation on the table rather than on the rows | |
* @link https://davidwalsh.name/event-delegate | |
*/ | |
document.querySelectorAll(tableSelector).forEach(function (table) { | |
table.addEventListener('click', function (elem) { | |
// click happens on the td/th element, need the parent | |
if (elem.target && elem.target.parentNode.matches('tr.'+parentClass)) { | |
toggleRowChildren(elem.target.parentNode); | |
} | |
}); | |
}); | |
} | |
}; | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
if (typeof jQuery === 'undefined') throw "jQuery Required"; | |
/** | |
* DOM ready execution | |
* | |
* @param {object} $ | |
* @returns {undefined} | |
* @link http://api.jquery.com/jQuery/#jQuery3 failsafe $ alias | |
* @licence MIT | |
*/ | |
jQuery(function ($) { | |
/** | |
* Hierarchical table rows object | |
* | |
* @type object | |
* @link http://www.dustindiaz.com/json-for-the-masses/ Functional vs Classy section | |
*/ | |
var treeTable = { | |
parentClass : 'header', | |
childClassPrefix : '', | |
collapsedClass : 'collapsed', | |
/** | |
* Set up the event handler | |
* | |
* @param {string} parentClass e.g. 'parent' | |
* @param {string} collapsedClass e.g. 'hidden' | |
* @param {string} childClassPrefix e.g. 'parentId' | |
*/ | |
init : function(parentClass, collapsedClass, childClassPrefix) { | |
if (parentClass !== undefined) { | |
this.parentClass = parentClass; | |
} | |
if (collapsedClass !== undefined) { | |
this.collapsedClass = collapsedClass; | |
} | |
if (childClassPrefix !== undefined) { | |
this.childClassPrefix = childClassPrefix; | |
} | |
/** | |
* event delegation on the table rather than on the rows | |
* @link http://24ways.org/2011/your-jquery-now-with-less-suck/ Delegation section | |
*/ | |
$('table').on('click', 'tr.'+treeTable.parentClass, function () { | |
treeTable.toggleRowChildren($(this)); | |
}); | |
}, | |
/** | |
* Recursively hide all child rows or show immediate children | |
* | |
* @param {object} parentRow jQuery row element object | |
*/ | |
toggleRowChildren : function(parentRow) { | |
// all direct children must have a class that is the same as the parent id with an optional prefix | |
var childClass = this.childClassPrefix+parentRow.attr('id'); | |
/** | |
* cache the children selection | |
* @link http://24ways.org/2011/your-jquery-now-with-less-suck/ Caching section | |
* | |
* use the parent row as 'selector context' | |
* @link http://api.jquery.com/jQuery/#selector-context | |
* | |
* use the faster element selector followed by a slower filter on the .childClass | |
* @link http://24ways.org/2011/your-jquery-now-with-less-suck/ Selector optimization section | |
* | |
*/ | |
var childrenRows = $('tr', parentRow.parent()).filter('.'+childClass); | |
// toggle all the children | |
childrenRows.toggle(); | |
// foreach of the children | |
childrenRows.each(function(){ | |
// if a child is a parent and isn't collapsed | |
if ($(this).hasClass(treeTable.parentClass) && !$(this).hasClass(treeTable.collapsedClass)) { | |
// recurse to the child | |
treeTable.toggleRowChildren($(this)); | |
} | |
}); | |
// 'mark' that the child has been hidden or not | |
parentRow.toggleClass(this.collapsedClass); | |
} | |
}; | |
// only this bit actually needs to wait for the DOM | |
treeTable.init(); | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE HTML> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<title>Toggle Row Children</title> | |
<style> | |
body { | |
color: #444; | |
font-size: 18px; | |
margin: 40px auto; | |
max-width: 720px; | |
line-height: 1.4; | |
font-family: sans-serif; | |
padding: 10px 16px; | |
} | |
h1,h2,h3 { | |
line-height: 1.2 | |
} | |
a { | |
text-decoration: none; | |
} | |
h1,h2,h3,h4,h5,h6 { | |
font-weight: normal; | |
} | |
table { | |
border-spacing: 0; | |
border-collapse: collapse; | |
width: 100%; | |
} | |
th, td { | |
border-top: 1px solid #444; | |
padding: 12px; | |
text-align: left; | |
} | |
th:hover { | |
cursor: pointer; | |
} | |
tr.active { | |
background-color: #f1f1f1; | |
} | |
</style> | |
</head> | |
<body> | |
<div class="container"> | |
<h1>Toggle Row Children</h1> | |
<p>Click on a header row to toggle</p> | |
<table> | |
<tr id="cat1" class="parent"> | |
<th>Cat1</th><th>Row</th> | |
</tr> | |
<tr class="child-cat1"> | |
<td> data1</td><td>data2</td> | |
</tr> | |
<tr id="cat1a" class="parent child-cat1"> | |
<th> Cat1a</th><th>Row</th> | |
</tr> | |
<tr class="child-cat1a"> | |
<td> data1</td><td>data2</td> | |
</tr> | |
<tr class="child-cat1a"> | |
<td> data3</td><td>data4</td> | |
</tr> | |
<tr id="cat2" class="parent"> | |
<th>Cat2</th><th>Row</th> | |
</tr> | |
<tr class="child-cat2"> | |
<td> data1</td><td>data2</td> | |
</tr> | |
</table> | |
</div> | |
<script type="text/javascript" src="row_toggle.js"></script> | |
<script type="text/javascript"> | |
(function () { | |
var myTreeTable = treeTable({ | |
parentClass: 'parent', | |
collapsedClass: 'active', | |
childClassPrefix: 'child-' | |
}); | |
// only this bit actually needs to wait for the DOM | |
document.addEventListener("DOMContentLoaded", myTreeTable.init.bind(myTreeTable)); | |
})(); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Setup
This works for multiple levels of children. It relies on the parent row
id
being the immediate child rowclass
. e.g.<tr id="cat1" class="header">
(parent)<tr id="cat1a" class="cat1 header">
(child & parent)<tr class="cat1a">
(grandchild)All parents need a 'parent' class, the default is
"header"
.and after clicking row
id=cat1
you would have<tr id="cat1" class="header collapsed">
(parent)<tr id="cat1a" class="cat1 header collapsed" style="display:none;">
(child & parent)<tr class="cat1a" style="display: none;">
(grandchild)Options
The
treeTable.init()
has the following options:parentClass
,collapsedClass
,childClassPrefix
, e.g.treeTable.init('parent', 'hideChildren', 'parentId')
could have the following structure:<tr id="1" class="parent">
(parent)<tr id="2" class="parentId1 parent">
(child & parent)<tr class="parentId2">
(grandchild)and after clicking row
id=1
you would have<tr id="1" class="parent hideChildren">
(parent)<tr id="2" class="parentId1 parent hideChildren" style="display:none;">
(child & parent)<tr class="parentId2" style="display:none;">
(grandchild)