I'm having troubles with saving the state of collapsed rows : I have child and parent rows in a table. Parents have a /- buttons allowing users to collapse/expand the child rows (which have a bigger data-level). The expand collapse itself works fine but if I collapse a row and then collapse the above row again, when I expand the higher row it expands everything after but I'd like to keep the collapsed rows collapsed.
In this example it works the way I'd like : Fiddle which works. If you for example collapse the second row, then the first one, and expand the first, the second and all its children are kept collapsed and hidden.
In this example (which I'm struggling with) it doesn't work anymore : Fiddle which doesn't work
Does anyone have an idea of what's the problem ? I can't see why it works in the first case but not the second.
Thanks in advance for your help ! Sincerely,
EDIT :
The data-level needs to be equal to the headers="h_M1"
td and each tr which have a lower data-level than its follower needs to be a parent that can be collapsed. For example in your edit there is tr3 as a child, but it needs to be a parent which can collapse all the tr after it (since there's no other 3 or lower after it). And it needs to stay collapsed if we collapse/expand tr2
// Expand/collapse when clicking on first column //
function getChildren($row) {
var children = [], level = $row.attr('data-level');
while($row.next().attr('data-level') > parseInt(level)) {
children.push($row.next());
$row = $row.next();
}
return children;
}
$('.colex').on('click', function() {
var action;
if($(this).parent('.parent').hasClass('collapsed')) {
$(this).parent('.parent').removeClass('collapsed');
$(this).text('-');
action = 'show';
} else {
$(this).parent('.parent').addClass('collapsed');
$(this).text(' ');
action = 'hide';
}
var level = $(this).parent('.parent').attr('data-level');
var children = getChildren($(this).parent('.parent'));
$.each(children, function() {
if(action == "show") {
if ($(this).attr('data-level')==parseInt(level) 1
|| $(this).hasClass('hidden')===false) {
/* un-hide direct children, and not hidden sub-children */
$(this).removeClass('hidden').show();
}
} else {
if ($(this).attr('data-level')==parseInt(level) 1) {
/* apply hidden to direct children */
$(this).addClass('hidden');
}
/* hide all children */
$(this).hide();
}
});
})
body {
font-family: sans-serif;
}
body * {
box-sizing: border-box;
/*color: #59547c;*/
margin: 0;
}
main {
/* max-width: 60rem;*/
margin: 1px auto;
padding: 1rem;
}
h1 {
margin-bottom: 1rem;
}
p {
margin-bottom: 1rem;
}
input.variante[type=checkbox] {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
}
input.variante[type=checkbox] {
height: 15px;
width: 15px;
background: #fff;
border: 1px solid #ccc;
}
input.variante[type="checkbox"]:checked
{
background: orange;
&:before {
font-family: FontAwesome;
content: '\f00c';
display: block;
color: grey;
font-size: 15px;
position: absolute;
}
}
table {
empty-cells: show;
}
th {
text-align:center;
border: 1px solid antiquewhite;
}
.parent > .colex {
color: black;
cursor: pointer;
}
.table_header {
background-color:#484e82;
color:#fdfdff;
font-weight: bold;
}
.intro {
font-size: 1.125rem;
}
.Titre_1 {
color: red;
}
.Titre_2 {
color: purple;
}
.Titre_3 {
color: fuchsia;
}
.Titre_4 {
color: green;
}
.Titre_5 {
color: lime;
}
.Titre_6 {
color: navy;
}
.Titre_7 {
color: blue;
}
.Titre_8 {
color: aqua;
}
.Titre_9 {
color: orange;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<body>
<intro>
<p >description</p>
</intro>
<main>
<table id="mytable" align=center rules=groups frame=box hborder=0 cellspacing=1 cellpadding=4 vborder=1>
<thead >
<th id="colex" width=2%>Co<br/>Ex</th>
<th id="h_M8" width=2%>V<br/>M8</th>
<th id="h_M1" width=2%>cd<br/>M1</th>
<th id="h_M2" width=5%>N°<br/>M2</th>
<th id="h_M4" width=23%>Désignation<br/>M4</th>
<th id="h_M7" width=3%>Uté<br/>M7</th>
<th id="h_M9" width=3%>Qté <br/>M9</th>
<th id="h_M18" width=5%>PR Fourniture unitaire<br/>M18</th>
<th id="h_M20" width=3%>Type FO<br/>M20</th>
<th id="h_M14" width=3%>K Acc<br/>M14</th>
<th id="h_M19" width=6%>PR Total fourniture<br/>M19</th>
<th id="h_M23" width=5%>Tps pose unitaire<br/>M23</th>
<th id="h_M26" width=3%>Type MO<br/>M26</th>
<th id="h_M22" width=3%>K Pose<br/>M22</th>
<th id="h_M42" width=5%>Tps pose total<br/>M42</th>
<th id="h_M40" width=5%>PR unitaire<br/>M40</th>
<th id="h_M41" width=5%>PR Total<br/>M41</th>
<th id="h_M27" width=3%>qte vendue<br/>M27</th>
<th id="h_M32" width=5%>PV unitaire<br/>M32</th>
<th id="h_M44" width=6%>PV Total<br/>M44</th>
</thead>
<tbody>
<tr data-level="1">
<td headers="colex" >-</td>
<td headers="h_M8"><input type="checkbox"></td>
<td headers="h_M1">1</td>
<td headers="h_M2">A</td>
<td headers="h_M4">Titre 1, n'appartient à personne</td>
<td headers="h_M7">ens</td>
<td headers="h_M9">1</td>
<td headers="h_M18"></td>
<td headers="h_M20"></td>
<td headers="h_M14"></td>
<td headers="h_M19"></td>
<td headers="h_M23"></td>
<td headers="h_M26"></td>
<td headers="h_M22"></td>
<td headers="h_M42"></td>
<td headers="h_M40"></td>
<td headers="h_M41"></td>
<td headers="h_M27"></td>
<td headers="h_M32"></td>
<td headers="h_M44"></td>
</tr>
<tr data-level="2">
<td headers="colex" >-</td>
<td headers="h_M8"><input type="checkbox"></td>
<td headers="h_M1">2</td>
<td headers="h_M2">A.1</td>
<td headers="h_M4">Titre 2 - la ligne doit appartenir au titre niveau 1, N°A ci-dessus</td>
<td headers="h_M7">ens</td>
<td headers="h_M9">1</td>
</tr>
<tr data-level="3">
<td headers="colex" >-</td>
<td headers="h_M8"><input type="checkbox"></td>
<td headers="h_M1">3</td>
<td headers="h_M2"></td>
<td headers="h_M4">cette ligne "terminale" doit appartenir au titre niveau 2, N°A.1 ci-dessus</td>
<td headers="h_M7">km</td>
<td headers="h_M9">6</td>
</tr>
<tr data-level="4">
<td headers="colex" >-</td>
<td headers="h_M8"><input type="checkbox"></td>
<td headers="h_M1">4</td>
<td headers="h_M2"></td>
<td headers="h_M4">cette ligne doit aussi appartenir au titre niveau 2, N°A.1 ci-dessus</td>
<td headers="h_M7">m</td>
<td headers="h_M9">10,5</td>
</tr>
<tr data-level="5">
<td headers="colex" ></td>
<td headers="h_M8"><input type="checkbox"></td>
<td headers="h_M1">5</td>
<td headers="h_M2">B</td>
<td headers="h_M4">un autre Titre 1, n'appartient à personne</td>
<td headers="h_M7">ens</td>
<td headers="h_M9">1</td>
</tr>
<tr data-level="5">
<td headers="colex" >-</td>
<td headers="h_M8"><input type="checkbox"></td>
<td headers="h_M1">5</td>
<td headers="h_M2">B.1</td>
<td headers="h_M4">Titre 2 - la ligne doit appartenir au titre niveau 1, N°B ci-dessus</td>
<td headers="h_M7">ens</td>
<td headers="h_M9">1</td>
</tr>
<tr data-level="6">
<td headers="colex" >-</td>
<td headers="h_M8"><input type="checkbox"></td>
<td headers="h_M1">6</td>
<td headers="h_M2">B.1.1</td>
<td headers="h_M4">Titre 3 - la ligne doit appartenir au titre niveau 2, N°B.1 ci-dessus</td>
<td headers="h_M7">ens</td>
<td headers="h_M9">1</td>
</tr>
<tr data-level="9">
<td headers="colex" ></td>
<td headers="h_M8"><input type="checkbox"></td>
<td headers="h_M1">9</td>
<td headers="h_M2"></td>
<td headers="h_M4">cette ligne "terminale" doit appartenir au titre niveau 3, N°B.1.1 ci-dessus</td>
<td headers="h_M7">kg</td>
<td headers="h_M9">2</td>
</tr>
<tr data-level="9">
<td headers="colex" ></td>
<td headers="h_M8"><input type="checkbox"></td>
<td headers="h_M1">9</td>
<td headers="h_M2"></td>
<td headers="h_M4">cette ligne doit aussi appartenir au titre niveau 3, N°B.1.1 ci-dessus</td>
<td headers="h_M7">m</td>
<td headers="h_M9">10,5</td>
</tr>
<tr data-level="9">
<td headers="colex" ></td>
<td headers="h_M8"><input type="checkbox"></td>
<td headers="h_M1">9</td>
<td headers="h_M2">B.1.2</td>
<td headers="h_M4">Titre 3 - la ligne doit appartenir au titre niveau 2, N°B.1 ci-dessus</td>
<td headers="h_M7">ens</td>
<td headers="h_M9">1</td>
</tr>
</tbody>
</table>
</main>
</body>
CodePudding user response:
updating the JS code as per your changes in question. this should do what you are trying to do.
// recursive method to get the child of the element
const gc = (a) => {
var children = [];
// loop till the data-level is lower than the parent node or it's a child at the same level.
while (
parseInt(a.next().attr("data-level")) > parseInt(a.attr("data-level")) ||
(parseInt(a.next().attr("data-level")) == parseInt(a.attr("data-level")) &&
a.hasClass("child"))
) {
// if next node is parent call the recursive method
if (a.next().hasClass("parent")) {
var temp = gc(a.next()); // store children in temp var.
children.push({
p: true,
c: temp.c,
n: a.next().get(0),
h: a.next().hasClass("collapsed"),
});
// push the children in array 'p' : to identify if node is parent, 'c' : all child to the element, 'n' : current node, 'h' : to identify if its colapsed.
a = temp.r; // move to the next row (jumping all child rows)
} else {
children.push({ p: false, n: a.next().get(0) }); // if the node is child push without anything
a = a.next(); // move to next row.
}
}
return { r: a.next(), c: children }; // return object 'r' : next row for iteration, 'c' : children of the node a
};
// method to hide/unhide row
const up = (item, show) => {
if (item.p && !item.h) for (var child of item.c) up(child, show); // if the node is parent and not collapsed update all children recursively on all children of the node.
show ? $(item.n).show() : $(item.n).hide(); // show/hide node
};
// on click method for colex tr
$(".colex").on("click", function () {
if ($(this).text() == "") return; // if child do nothing
var show = $(this).text() == " "; // set if to show or hide on basis of icon value ' ' : show, '-' : hide
$(this).text(show ? "-" : " "); // switch the icon and add/remove the collapsed class
$(this).parent(".parent").toggleClass("collapsed"); // toggle class collapsed
for (var child of gc($(this).parent(".parent")).c) up(child, show); // iterate on all children and show/hide them
});
Let me know if you don't understand anything about it.