I am looking for an efficient way to expand/collaps开发者_运维知识库e hierarchical table rows using jQuery. The problem is, that the expand and collapse functionality differs.
- Initially, only rows with class
level_0
are show, all other rows are hidden. - expand should only show the next level, so clicking on row
id=10
should only make rows withid=11
andid=14
visible. - collapse on the other hand, should collapse all consecutive rows with a deeper level than the current one. For example, clicking collapse on row
id=10
should hide rows with ids11, 12, 13, 14
, if they are visible.
The table data looks as follows:
<table id="mytable">
<tr class="level_0" id="10">...</td>
<tr class="level_1 parent_10" id="11">...</td>
<tr class="level_2 parent_11" id="12">...</td>
<tr class="level_2 parent_11" id="13">...</td>
<tr class="level_1 parent_10" id="14">...</td>
<tr class="level_0" id="15">...</td>
</table>
My non-working solution:
$('#mytable tr').live('click', function() {
var toggleClass = 'parent_' + $(this).attr('id');
$(this).nextAll('tr').each(function() {
if ($(this).is('.'+toggleClass)) {
$(this).toggleClass("showme");
}
});
});
The problem is, that it only collapses the next level rows. Visible and deeper level rows beneath the clicked row are still shown.
Can anyone give me some hints on how I could do this in an efficient way? The HTML code can be adjusted if needed.
Thanks for any hints.
The table html in your post is not valid (tr enclosed by td). On the properly structured table, this code works.
$("tr.level_0").live("click", function () {
$(this).nextUntil(".level_0").toggle();
});
I think you're gonna need a little some more code so you can handle clicks on all the rows in both closed and open states.
I added a class switch to indicate when a row is open, and treat clicks differently based on it's state. Both collapse
and expand
have while loops that walk through rows, starting with the one immediately after the clicked row, and they stop when they get to rows of the same level. This logic should work for any level, not just 0. Also, the while
loop could probably be cleaner using nextUntil
logic with a fancy selector - I wasn't familiar with that jQuery method until seeing Jules answer - very slick!
ALso, for keeping this example code simpler, I treated your level class naming system as HTML data attributes, so a given row looks like this: <tr data-level="0" id="10">
. You could replace calls to .data
with code to parse your class names.
var $rows = $('#mytable tr');
$rows.live('click', function() {
$(this).toggleClass('open');
if ($this.hasClass('open')){
collapse($(this));
} else {
expand($this);
}
}
function collapse ($row) {
$row.removeClass('open');
var rowIndex = $rows.index($row);
var siblingOrAncestorRowFound = false;
while (!siblingOrAncestorRowFound){
var $nextRow = $rows.eq(rowIndex + 1);
if ($nextRow.level > $row.level){
$nextRow.hide().removeClass('open');
rowIndex++;
} else {
siblingOrAncestorRowFound = true;
}
}
}
function expand ($row) {
$row.addClass('open')
var rowIndex = $rows.index($row);
var siblingOrAncestorRowFound = false;
while (!siblingOrAncestorRowFound){
var $nextRow = $rows.eq(rowIndex + 1);
if ($nextRow.level > $row.level){
// only show rows exactly one level below
if ($nextRow.level == $row.level + 1){
$nextRow.show();
}
rowIndex++;
} else {
siblingOrAncestorRowFound = true;
}
}
}
(This isn't tested - sorry gotta hit the sack!)
I've created a version for multiple levels of hierarchy as an answer to another question.
The jQuery for your table would be:
var treeTable = {
parentClassPrefix : '',
collapsedClass : 'collapsed',
init : function(parentClassPrefix) {
this.parentClassPrefix = parentClassPrefix;
$('table').on('click', 'tr', function () {
treeTable.toggleRowChildren($(this));
});
},
toggleRowChildren : function(parentRow) {
var childClass = this.parentClassPrefix+parentRow.attr('id');
var childrenRows = $('tr', parentRow.parent()).filter('.'+childClass);
childrenRows.toggle();
childrenRows.each(function(){
if (!$(this).hasClass(treeTable.collapsedClass)) {
treeTable.toggleRowChildren($(this));
}
});
parentRow.toggleClass(this.collapsedClass);
}
};
treeTable.init('parent_');
See this JSFiddle for it working.
Optimisations
I have a slightly different table structure, which involves specifying parents as well as children so that the searching can be more efficient.
I've then also made a jQuery hierarchical table row toggling Gist from it and converted it into objectified javascript with optimisations. The optimisations come from http://24ways.org/2011/your-jquery-now-with-less-suck/.
Specifically:
Event delegation on the table rather than on the rows.
$('table').on('click', 'tr.'+treeTable.parentClass, function () {
treeTable.toggleRowChildren($(this));
});
Cache the children selection.
Use the faster element selector followed by a slower filter on the .childClass
var childrenRows = $('tr', parentRow.parent()).filter('.'+childClass);
精彩评论