I am looking for an efficient way to expand/collapse hierarchical table rows using jQuery. The problem is, that the expand and collapse functionality differs.
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: (This isn't tested - sorry gotta hit the sack!). 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;
}
}
}