Isotope: combination filtering + multiple selection

孤街浪徒 提交于 2019-12-03 21:47:55
DrewT

Using multiple filter types with metafizzy isotope filtering requires a multi-dimensional array to hold an array of each filter type.

I have a live example here

And js for the example is here

For a full code explanation see my answer to this question

baaroz

have a look here

http://codepen.io/desandro/pen/btFfG

it's the same answer by DrewT but from the Isotope team.

all you have to do in order to filter is put every group under same object

        var filters = {};//sould be outside the scope of the filtering function


  $a.click(function(){//filtering function
                var group = $optionSet.attr('data-filter-group');
             var filterGroup = filters[group];
                              if (!filterGroup) {
                                  filterGroup = filters[group] = [];
                              }
         filters[ group ].push($this.attr('data-filter-value'));
  })

now all you have to do is call getComboFilter function(you don't have to understand the code inside getComboFilter, think of it as a part of isotope)

     var comboFilter = getComboFilter( filters );
 $container.isotope({ filter: comboFilter});

function getComboFilter( filters ) {
  var i = 0;
  var comboFilters = [];
  var message = [];

  for ( var prop in filters ) {
    message.push( filters[ prop ].join(' ') );
    var filterGroup = filters[ prop ];
    // skip to next filter group if it doesn't have any values
    if ( !filterGroup.length ) {
      continue;
    }
    if ( i === 0 ) {
      // copy to new array
      comboFilters = filterGroup.slice(0);
    } else {
      var filterSelectors = [];
      // copy to fresh array
      var groupCombo = comboFilters.slice(0); // [ A, B ]
      // merge filter Groups
      for (var k=0, len3 = filterGroup.length; k < len3; k++) {
        for (var j=0, len2 = groupCombo.length; j < len2; j++) {
          filterSelectors.push( groupCombo[j] + filterGroup[k] ); // [ 1, 2 ]
        }

      }
      // apply filter selectors to combo filters for next group
      comboFilters = filterSelectors;
    }
    i++;
  }

  var comboFilter = comboFilters.join(', ');
  return comboFilter;
}

Here is the JS Fiddle code you can follow for Isotope Combination Filter with multiple select dropdown

$(document).ready(function(){
  // init Isotope
  var $grid = $('.grid').isotope({
    itemSelector: '.item'
  });

  // store filter for each group
  var filters = {};

  $('.filters').on('change', '.filters-select', function(){
    var $this = $(this);
    var filterGroup = $this.attr('data-filter-group');
    filters[ filterGroup ] = $this.val();
    var filterValue = concatValues( filters );
    $grid.isotope({ filter: filterValue });
  });

  // flatten object by concatting values
  function concatValues( obj ) {
    var value = '';
    for ( var prop in obj ) {
      value += obj[ prop ];
    }
    return value;
  }
});

https://jsfiddle.net/srikanthLavudia/vhrbqn6p/

I could make it work but it was not as straightforward as I thought in a firts instance. I think you need to bear on mind a couple of things:

  1. If you want to use a restrictive AND condition, then I suggest don´t use the "data-filter-value" option. Instead, put your filter variables as classes. Then, you will need to fire the isotope function by yourself in the onclick event. This is because otherwise you will fire the "default" isotope function when the user clicks the button and you won´t be able to achieve the restrictive AND mentioned.

  2. In the same direction, you will need to use new variable names (so new classes) for each combination of nested filter options you are going to work with. Otherwise, I don´t see other way (at the moment) to get a restrictive AND condition.

This concept about restrictive and nonrestrictive AND clauses is like the difference between an outer and an inner join. It's the concept you should use if you want to handle two filters, one subfilter of the other one.
One example would be something like a list of ebooks which could be filtered by brand, an other subfilter based on the different range prices they belong to.

I show you below a code example where I implement this idea, it can give you a hint of what i mean:

HTML:

<!--Filters section -->
<div id="filters">
    <ul id="optionSet1" class="option-set" data-option-key="filter">
           <li><a id="filter10" href="#filter" class="selected">Everything</a></li>
        <c:forEach var="brand" items="${brands}" varStatus="status" >
             <li><a id="filter1${status.count}" href="#filter" class='${brand}'>${brand}</a></li>
        </c:forEach>
    </ul>
    <ul id="optionSet2" class="option-set" data-option-key="filter">
        <li><a id="filter20" href="#filter" class="selected">Any Price</a>  </li>
    <c:forEach var="brandPrice" items="${brandPrices}" varStatus="status" >
        <li><a id="filter2${status.count}" href="#filter" class='${brandPrice}'>${brandPrice}</a></li>
    </c:forEach>
    </ul>
</div>  


<!--Elements to filter -->
<div id="portfolio-wrapper" class="row">
    <c:forEach var="publication" items="${publications}" varStatus="status" >               
        <div class='span4 portfolio-item ${publication.filterOption1} ${publication.filterOption2} ${publication.filterOption3}...'>
<!-- filterOption3 would be the combination of filterOption1 and filterOption2, you can make this building a string as the addition of filterOption1 and filterOption2 strings-->
            <!--Content to display-->           
    </c:forEach>
</div>

javascript:

$(function(){

        $("a[id^='filter1']").on('click', function() {
//          alert($(this).attr('class'));
//          alert($('#optionSet2 .selected').attr('class'));
            var myclass = $('#optionSet2 .selected').attr('class');
            myclass = myclass.replace(' selected','');
            myclass = myclass.replace('selected','');
            var myclass2 = $(this).attr('class');
            myclass2 = myclass2.replace(' selected','');
            myclass2 = myclass2.replace('selected','');
            if($(myclass=='' && myclass2==''){
                $('#portfolio-wrapper').isotope({ filter: '*'});
            }else if(myclass==''){
                $('#portfolio-wrapper').isotope({ filter: '.'+ myclass2+'' });
            }else if(myclass2==''){
                $('#portfolio-wrapper').isotope({ filter: '.'+myclass+'' });
            }else{

                $('#portfolio-wrapper').isotope({ filter: '.'+myclass2+myclass+''});
            }   
        });

        $("a[id^='filter2']").on('click', function() {
//          alert($(this).attr('class'));
//          alert($('#optionSet1 .selected').attr('class'));
            var myclass = $('#optionSet1 .selected').attr('class');
            myclass = myclass.replace(' selected','');
            myclass = myclass.replace('selected','');
            var myclass2 = $(this).attr('class');
            myclass2 = myclass2.replace(' selected','');
            myclass2 = myclass2.replace('selected','');
            if(myclass=='' && myclass2==''){
                $('#portfolio-wrapper').isotope({ filter: '*'});
            }else if(myclass==''){
                $('#portfolio-wrapper').isotope({ filter: '.'+ myclass2+'' });
            }else if(myclass2==''){
                $('#portfolio-wrapper').isotope({ filter: '.'+myclass+'' });
            }else{

                $('#portfolio-wrapper').isotope({ filter: '.'+myclass+myclass2+''});
            }
        });

});

Sorry to dredge this question up, but I recently ran into the same problem and (imho) wasted a lot of time solving this with combined selectors. (Combine every class from array A with every class from array B, etc.)

Use a filtering function. It allows arbitrary complexity and is probably the right choice for all non-trivial cases.

Most importantly, there is no performance benefit to using selectors (which you might expect if the selector were fed directly into a querySelector or jQuery call). Isotope iterates through each item and applies the selector as a function regardless:

return function( item ) {
  return matchesSelector( item.element, filter );
};

Therefore, you might as well just put your filtering logic into a function instead of trying to generate a selector string.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!