Contraer y expandir filas de la tabla que no están anidadas

Tengo una tabla con filas que no están anidadas aunque parezcan estar. No puedo hacer cambios en este diseño, esto es con lo que tengo que trabajar.

Las filas con el + colapsan las filas debajo de ellas y las filas “anidadas” colapsaron las filas debajo de ellas, etc. Mi solución funciona hasta cierto punto. El problema que tengo es que la segunda fila “anidada” se colapsa cada vez debajo de ella, incluidas las filas que no debería. Sé por qué, pero no puedo encontrar la manera de resolverlo.

La mejor manera de ver de qué estoy hablando es jugando con el violín. Verás lo que quiero decir.

http://jsfiddle.net/pc1sp8L6/

EDITAR

Tengo que poder reutilizar esta función, así que tengo que mantenerla genérica. Es por eso que no estoy usando clases específicas, etc.

HTML

+Heading value value value value
Heading value value value value
Heading value value value value
Heading value value value value
+Heading value value value value
Heading value value value value
Heading value value value value
Heading value value value value
+Heading value value value value
+Heading value value value value
Heading value value value value
Heading value value value value
Heading value value value value
+Heading value value value value
Heading value value value value
Heading value value value value
+Heading value value value value
Heading value value value value
Heading value value value value
Heading value value value value

JS

 (function() { var toggle = $('span.toggle'); toggle.on('click', function() { var $this = $(this); var objectClass = $this.parent().parent().attr('class'); toggleRow($this, objectClass); }); function toggleRow(element, elClass) { var classes = elClass.split(' '); var textList = ''; for (var i = 0; i  0) { textList += "."+classes[i]; } } var $this = element.parents(textList); if(!$this.hasClass('collapsed')) { $this.addClass('collapsed').nextUntil('tr' + textList).hide(); } else { $this.removeClass('collapsed').nextUntil('tr' + textList).show(); } } }()); 

 $('.parent-row').click(function(){ var $element = $(this).next(); while(!$element.hasClass('parent-row')){ $element.toggle(); if($element.next().length >0){ $element = $element.next(); } else{ return; } } }); $('.child-row.has-children').click(function(){ var $element = $(this).next(); while($element.hasClass('child-child-row')){ $element.toggle(); if($element.next().length >0){ $element = $element.next(); } else{ return; } } }); 

https://jsfiddle.net/pc1sp8L6/2/

O una solución más genérica podría verse así

 $('tr[class]').click(function(){ var thisclass = $(this).attr("class"); var $element = $(this).next(); while(!$element.hasClass(thisclass)){ $element.toggle(); if($element.next().length >0){ $element = $element.next(); } else{ return; } } }); 

https://jsfiddle.net/pc1sp8L6/3/

No creo que esta sea la forma mejor / más limpia de hacer esto, pero hace lo que necesito.

 function toggleRow(element, elClass) { var classes = elClass.split(' '); var textList = ''; for (var i = 0; i < classes.length; i++) { if (classes[i].length > 0) { textList += "."+classes[i]; } } var $this = element.parents(textList); var lastRow = $this.index() == $('tr' + textList).last().index() ? true : false; if($this.attr('data-collapse') !== 'collapsed') { if(!lastRow) { $this.attr('data-collapse', 'collapsed').nextUntil('tr' + textList).hide(); } else { $this.attr('data-collapse', 'collapsed').nextUntil('tr.has-children').hide(); } } else { if(!lastRow) { $this.attr('data-collapse', '').nextUntil('tr' + textList).show(); } else { $this.attr('data-collapse', '').nextUntil('tr.has-children').show(); } } } 

http://jsfiddle.net/4nvcn00a/