Home > Software engineering >  How to save state of collapsed children rows (JS)
How to save state of collapsed children rows (JS)

Time:03-19

I'm trying to make a collapsable table for a project, and so far I'm succeeding pretty well. I'm only encountering one problem that I can't figure how to manage : Actually my collapsable rows (the ones which have children) are collapsing, but if I collapse a child, then the parent, and then I expand the parent, the children is expanded as well. How can I save the state of the children so that they don't expand when we expand the parent?

$(document).ready(function() {

// Define color depending on the M1 cell value //
    var rows = document.getElementsByTagName("tbody")
        [0].getElementsByTagName("tr");

    // loops through each row
    for (i = 0; i < rows.length; i  ) {
        cells = rows[i].getElementsByTagName('td');

        if (cells[2].innerHTML == '1')
            $(cells).parent().addClass('Titre_1');

        if (cells[2].innerHTML == '2')
            $(cells).parent().addClass('Titre_2');  

        if (cells[2].innerHTML == '3')
            $(cells).parent().addClass('Titre_3');

        if (cells[2].innerHTML == '4')
            $(cells).parent().addClass('Titre_4'); 

        if (cells[2].innerHTML == '5')
            $(cells).parent().addClass('Titre_5');

        if (cells[2].innerHTML == '6')
            $(cells).parent().addClass('Titre_6'); 

        if (cells[2].innerHTML == '7')
            $(cells).parent().addClass('Titre_7');

        if (cells[2].innerHTML == '8')
            $(cells).parent().addClass('Titre_8');   

        if (cells[2].innerHTML == '9')
            $(cells).parent().addClass('Titre_9');     
    }

// Expand/collapse when clicking on first column //
    function getChildren($row) {
        var children = [], level = $row.attr('data-level');
        while($row.next().attr('data-level') > 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 children = getChildren($(this).parent('.parent'));
        $.each(children, function() {
            if(action == "show") {
                $(this).removeClass('collapsed').show();
            } else {
                $(this).addClass('collapsed').hide();
                if($(this).children('.colex').text() == ' ') { // Replace possible ' ' by a '-' when parent's collapsed //
                    $(this).children('.colex').text('-');
                }
            }
        });
    })
})     
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;
}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Devis</title>
    <link rel="stylesheet" href="style.css">
</head>
<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="0">
                    <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="1">
                    <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="2">
                    <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 2, N°A.1 ci-dessus</td>
                    <td headers="h_M7">km</td>
                    <td headers="h_M9">6</td>
                </tr>
                <tr  data-level="2">
                    <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 2, N°A.1 ci-dessus</td>
                    <td headers="h_M7">m</td>
                    <td headers="h_M9">10,5</td>
                </tr>
                <tr  data-level="0">
                    <td headers="colex" >-</td>
                    <td headers="h_M8"><input  type="checkbox"></td>
                    <td headers="h_M1">1</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="1">
                    <td headers="colex" >-</td>
                    <td headers="h_M8"><input  type="checkbox"></td>
                    <td headers="h_M1">2</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="2">
                    <td headers="colex" >-</td>
                    <td headers="h_M8"><input  type="checkbox"></td>
                    <td headers="h_M1">3</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="3">
                    <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="3">
                    <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="2">
                    <td headers="colex" ></td>
                    <td headers="h_M8"><input  type="checkbox"></td>
                    <td headers="h_M1">3</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>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="voctor.js"></script>

</body>
</html> 

Here's the fiddle

CodePudding user response:

If I correctly understood your issue, you want to be able to open a "parent" without opening its children.

But then, why did you write your code to do so ?

See the culprit in the code bellow:

        var children = getChildren($(this).parent('.parent'));
        $.each(children, function() {
            if(action == "show") {
                /* HERE : This line only purpose is to display the children when a parent is displayed */
                $(this).removeClass('collapsed').show(); // <-- DELETE THIS
            } else {
                $(this).addClass('collapsed').hide();
                if($(this).children('.colex').text() == ' ') { // Replace possible ' ' by a '-' when parent's collapsed //
                    $(this).children('.colex').text('-');
                }
            }
        });

EDIT: Since your requirements have changed you need to review your logic.

What we know:

  • only parents can be collapsable
  • opening parents should only reveal their direct children AND the sub-children that were not previously hidden

To achieve this behavior, we'll need to make a difference between a collapsed parent AND its hidden children.

See the changes in the code :

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();
    }
});

I edited your Fiddle with the new code if you want to test it.

I also replaced your multiple if statements with a single line :

$(cells).parent().addClass('Titre_' cells[2].innerHTML);
  • Related