Michael_B Michael_B - 2 months ago 10
CSS Question

How to toggle elements in and out of DOM using jQuery detach() method?

I have a group of divs arranged in a grid.

enter image description here

To style each div I'm selecting them with the

pseudo-class.

div.tile:nth-child(4n-7) .text { background-color: yellow; }


A user can hide divs by clicking on a button (which fires a jQuery function that adds a
display: none
rule to the
class
attribute in the selected div).

jQuery

$('.hide-divs').click(function () {
$('.dolphin').toggleClass('hidden');
})


CSS

.hidden { display: none; }


Here's the problem:

Even though
display: none
removes the div from the screen, it doesn't remove the div from the DOM, so the
nth-child
selector still counts it when applying styles, which in turn messes up the visual design of the grid.

enter image description here

The above layout is broken because only the first column should be yellow.

So my first thought was to use the jQuery
remove()
method
, which takes elements (and its descendants) out of the DOM.

Turns out, however, once
remove()
is applied you can't get those divs back. They're gone. So the toggle function breaks.

After a bit of research I discovered the jQuery
detach()
method
, which does the same thing as
.remove()
, except it stores the removed elements' data for later use.

From the jQuery website:


The
.detach()
method is the same as
.remove()
, except that
.detach()
keeps all jQuery data associated with the removed
elements. This method is useful when removed elements are to be
reinserted into the DOM at a later time.


Everything looks good for
detach()
to work with the toggle switch, except my efforts to implement it aren't working.

I've used the example on the jQuery website as a guide but it doesn't work on the grid. I also read various related posts on this site, but to no avail. I must be missing something.

Any feedback would be much appreciated.

http://jsfiddle.net/tfyfpbb2/



$('.hide-divs').click(function() {
$('.dolphin').toggleClass('hidden');
})

.row {
display: flex;
flex-wrap: wrap;
width: 500px;
padding: 0;
margin: 0;
}
.text {
height: 50px;
width: 100px;
margin: 10px;
padding-top: 15px;
background: tomato;
color: #fff;
text-align: center;
font-size: 2em;
}
.tile:nth-child(4n-7) .text {
border: 2px solid #ccc;
background-color: yellow;
color: #000;
}
button {
padding: 10px;
background-color: lightblue;
position: relative;
left: 200px;
}
.hidden {
display: none;
}

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">

<div class="row">

<div class="tile">
<div class="text">01</div>
</div>

<div class="tile">
<div class="text">02</div>
</div>

<div class="tile">
<div class="text dolphin">03</div>
</div>

<div class="tile">
<div class="text">04</div>
</div>

<div class="tile">
<div class="text">05</div>
</div>

<div class="tile">
<div class="text dolphin">06</div>
</div>

<div class="tile">
<div class="text">07</div>
</div>

<div class="tile">
<div class="text dolphin">08</div>
</div>

<div class="tile">
<div class="text">09</div>
</div>

<div class="tile">
<div class="text">10</div>
</div>

<div class="tile">
<div class="text">11</div>
</div>

<div class="tile">
<div class="text">12</div>
</div>

</div><!-- end .row -->

<button type="button" class="hide-divs">HIDE DIVS 3, 6 &amp; 8</button>




Answer

I suggest still using detach. You can do so with this code:

var divs;

$('.hide-divs').on('click', function () {
    if(divs) {
        $(divs).appendTo('.row');
        divs = null;
    } else {
        divs = $('.dolphin').parent().detach();
    }
});

Then to ensure that the same order is used, I came up with this bit of code:

$('.tile').each(function(i){
    $(this).data('initial-index', i);
});

...

$(divs).each(function(){
    var oldIndex = $(this).data('initial-index');
    $('.tile').eq(oldIndex).before(this);
});

Put it all together and we get this code:

var divs;

$('.tile').each(function(i){
    $(this).data('initial-index', i);
});

$('.hide-divs').on('click', function () {
    if(divs) {
        $(divs).appendTo('.row').each(function(){
            var oldIndex = $(this).data('initial-index');
            $('.tile').eq(oldIndex).before(this);
        });
        divs = null;
    } else {
        divs = $('.dolphin').parent().detach();
    }
});

Demo on jsfiddle: http://jsfiddle.net/tqbzff2v/2/