Search code examples
jquerydomfilterdetach

How to filter elements out of DOM using jQuery and detach()


i'm using:

$('.filter__selector').change(function(){
var sel = $(this).val();
$('.filterItem').hide();
    //console.log(sel);
  if(sel != "all"){
    $('main').find('.projects__entry').hide();
    $('main').find('.' + sel).show();
  }
  else {
    $('main').find('.projects__entry').show();
  }
}
);

to filter list items with an select dropdown which works pretty good. the problem is that i'm using the &:nth-child(3n+3) to style every third list-item differently.

while hide() removes the items from the screen, it doesn't remove them from the DOM and my css gets screwed. as extra difficulty i want to toggle them back in if the select dropdown is changed.

for clarification i've created a fiddle: https://jsfiddle.net/9wtt4fge/2/ - the items in the third column should always get blue borders the items in the first two columns have to be red.

i've already found the detach() method and this question/answer How to toggle elements in and out of DOM using jQuery detach() method? but i can't make it work.

any help is appreciated!


Solution

  • The detach method is effectively the solution but the second part of the problem is that when you want to re-append it to the dom you have to know where, so you have to keep track of where you want to insert it. You have two choices, you can rely on an object which will keep your elements ordered and you'll have to detach and re-insert all in order with a loop, or you can rely on a comment element that you can insert just before your dom element.

    //create comments nodes to keep track of order
    let projectEntryElements = $('main .projects__entry');
    projectEntryElements.each(function(){
        let commentDom = $('<!--filter-track-comment-->').insertBefore(this);
        $(this).data('commentDom',commentDom);
    });
    
    $('.filter__selector').change(function(){
        let sel = $(this).val();
        $('.filterItem').hide(); //I don't know if you want to hide it or detach it so I let it as it was in your original code
        if(sel!="all"){
            projectEntryElements.detach();
            projectEntryElements.filter('.' + sel).each(function(){
                $(this).data('commentDom').after(this); //re-insert this after the comment node
            });
        }
        else {
            projectEntryElements.each(function(){
                $(this).data('commentDom').after(this); //re-insert this after the comment node
            });
        }
    
    });
    

    this example assume that ".projects__entry" are loaded in dom before the first part of this script, you have to adapt it to your use case (you can also replace the let keyword by var if you want retro-compat)