Thank you! Your feedback has been delivered
Thank you! Your feedback has been sent

latest isotope multiple filters where some filter elements are greater than X numbers

Need some help with the JS for an Isotope project. I want to use the latest isotope code from metafizzy. I can make multiple filters work, just not multiple filters when I need to filter on equal to or greater than numbers.

The application is to filter real estate listings by # of bedrooms, # of bathrooms, square footage, etc. When the site visitor clicks on 3+ bedrooms, I want Isotope to HIDE any any of the listings that have less than three bedrooms but SHOW any of the listings that have 3 OR MORE bedrooms. That's easy enough using isotope docs if you only have one filter criteria, but I want to have several.

I want Isotope to allow for multiple filters, i.e. one for bedrooms, one for bathrooms, one for square footage, and I want each filter to work as described above.

For example if someone wants to see all the listings with 3 or more bedrooms and 3 or more bathrooms with 3000+ square feet, I want Isotope to only show those items and hide the rest.

I can provide an example URL of what I have so far as a starting point and template for the format I'd need the JS to follow.

User Gravatar

brianbmg

Posted Jun 23 2014 14:30 UTC

$25


  • Assigned To Gaby
  • Solved
  • isotope
    multiple filter
  • 2454 Views

5 Replies


demo URL is http://www.honorsproperties.com/multi_filter.html

existing JS is as follows but it only allows sorting on the discrete searched for item, i.e. 3 bedrooms, 4 bedrooms, etc: $( function() { // init Isotope var $container = $('.isotope').isotope({ itemSelector: '.portfolio-item-wrapper' });

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

$('#filters').on( 'click', '.button', function() { var $this = $(this); // get group key var $buttonGroup = $this.parents('.button-group'); var filterGroup = $buttonGroup.attr('data-filter-group'); // set filter for group filters[ filterGroup ] = $this.attr('data-filter'); // combine filters var filterValue = ''; for ( var prop in filters ) { filterValue += filters[ prop ]; } // set filter for Isotope $container.isotope({ filter: filterValue }); });

// change is-checked class on buttons $('.button-group').each( function( i, buttonGroup ) { var $buttonGroup = $( buttonGroup ); $buttonGroup.on( 'click', 'button', function() { $buttonGroup.find('.is-checked').removeClass('is-checked'); $( this ).addClass('is-checked'); }); });

});

User Gravatar

brianbmg

Posted Jun 23 2014 15:03 UTC

You can see a working demo at codepen

I have done some changes though.

I have changed the filters to have data-filter-min and data-filter-max values, and also set each property on each isotope item as values instead of classes. This way we can do direct number comparisons instead of trying to match css classes.

Sample item HTML

<div class="isotope-item" data-beds="2" data-baths="2" data-sqf="2500">

Sample filter HTML

<div class="button-group js-radio-button-group" data-filter-group="sqf">
      <button class="is-checked">any</button>
      <button data-filter-min="1500" data-filter-max="2000">1500-2000</button>
      <button data-filter-min="2001" data-filter-max="3000">2001-3000</button>
      <button data-filter-min="3001" data-filter-max="4000">3001-4000</button>
      <button data-filter-min="4001" data-filter-max="5000">4001-5000</button>
      <button data-filter-min="5001">5000+</button>
</div>

Now when we click on a filter we do

var largestInt = 9007199254740992;

$('#filters').on( 'click', '.button', function() {
    var $this = $(this);
    // get group key
    var $buttonGroup = $this.parents('.button-group');
    var filterGroup = $buttonGroup.data('filter-group');
    // set filter for group
    filters[ filterGroup ] = {
       min: +$this.data('filter-min') || -largestInt, 
       max: +$this.data('filter-max') ||  largestInt
    };

    $container.isotope();
  });

And the isotope code is

 var $container = $('.isotope').isotope({
    itemSelector: '.isotope-item',
    filter: checkIfFilterApplies
  });


  function checkIfFilterApplies(){
      var self = $(this),
          match = true,
          data = self.data();
      for ( var prop in filters ) {
        match = (filters[prop].min <= data[prop] && filters[prop].max >= data[prop]);
        if (!match) return false;
      }
      return match;
  }

(I have used dummy html to simplify the demo)

Let me know if there is something you do not understand or need help with.

User Gravatar

Gaby

Posted Jun 23 2014 16:54 UTC

Solution

This didn't solve your task? Get your own custom solution.

Since you are using discrete groupings, you can simply add classes to allow items with higher values to appear when viewing that value or lower.

For example,

<div class=" portfolio-item-wrapper col-xs-12 col-sm-6 col-md-3 beds4 baths2 sqfN/A" style="position: absolute; left: 285px; top: 0px;">

will become

<div class=" portfolio-item-wrapper col-xs-12 col-sm-6 col-md-3 beds4 beds3 beds2 beds1 baths2 baths1 sqfN/A" style="position: absolute; left: 285px; top: 0px;">

and the item in question will appear when filtering for 1, 2, 3, or 4 beds and 1 or 2 baths.

User Gravatar

John

Posted Jun 23 2014 17:09 UTC

Just change your button html to this:

  <div class="ui-group">
    <h4>Bedrooms</h4>
    <div class="button-group js-radio-button-group" data-filter-group="beds">
      <button class="btn button is-checked" data-filter="">any</button>
      <button class="btn button" data-filter=".beds1, .beds2, .beds3, .beds4, .beds5">1+</button>
      <button class="btn button" data-filter=".beds2, .beds3, .beds4, .beds5">2+</button>
      <button class="btn button" data-filter=".beds3, .beds4, .beds5">3+</button>
      <button class="btn button" data-filter=".beds4, .beds5">4+</button>
      <button class="btn button" data-filter=".beds5">5</button>
    </div>
  </div>

  <div class="ui-group">
    <h4>Baths</h4>
    <div class="button-group js-radio-button-group" data-filter-group="baths">
      <button class="btn button is-checked" data-filter="">any</button>
      <button class="btn button" data-filter=".baths1, .baths2, .baths3, .baths4, .baths5">1+</button>
      <button class="btn button" data-filter=".baths2, .baths3, .baths4, .baths5">2+</button>
      <button class="btn button" data-filter=".baths3, .baths4, .baths5">3+</button>
      <button class="btn button" data-filter=".baths4, .baths5">4+</button>
      <button class="btn button" data-filter=".baths5">5</button>
    </div>
  </div>

  <div class="ui-group">
    <h4>Square Feet</h4>
    <div class="button-group js-radio-button-group" data-filter-group="sqf">
      <button class="btn button is-checked" data-filter="">any</button>
      <button class="btn button" data-filter=".sqf1500-2000">1500-2000</button>
      <button class="btn button" data-filter=".sqf2001-3000">2001-3000</button>
      <button class="btn button" data-filter=".sqg3001-4000">3001-4000</button>
      <button class="btn button" data-filter=".sqf4001-5000">4001-5000</button>
      <button class="btn button" data-filter=".sqf5000+">5000+</button>
    </div>
  </div>

Just change your JS to this:

$( function() {
  // init Isotope
  var $container = $('.isotope').isotope({
    itemSelector: '.portfolio-item-wrapper'
  });

  // store filter for each group
  var filters = {"beds": "", "baths": "", "sqf": ""};
  var bedsFil = [];
  var bathsFil = [];
  var sqfFil = "";
  $('#filters').on( 'click', '.button', function() {
    var $this = $(this);
    // get group key
    var $buttonGroup = $this.parents('.button-group');
    var filterGroup = $buttonGroup.attr('data-filter-group');
    $buttonGroup.find('.is-checked').removeClass('is-checked');
    $this.addClass('is-checked');
    // set filter for group
    filters[ filterGroup ] = $this.attr('data-filter');
    // combine filters
    bedsFil = filters["beds"].split(", ");
    bathsFil = filters["baths"].split(", ");
    sqfFil = filters["sqf"];
    filterValue = "";
    for (var bed in bedsFil) {
      for (var bath in bathsFil) {
        filterValue += bedsFil[bed]+bathsFil[bath]+sqfFil+", "
      }
    }
    // set filter for Isotope
    $container.isotope({ filter: filterValue.substr(0, filterValue.length-2) });
  });

});

I just did the logic with brute force so if you want "3+ beds, 2+ baths, and 2001-3000 sqft" it will create a filter string with these selectors:

".beds3.baths3.sqf2001-3000, .beds3.baths4.sqf2001-3000, .beds3.baths5.sqf2001-3000, .beds4.baths3.sqf2001-3000, .beds4.baths4.sqf2001-3000, .beds4.baths5.sqf2001-3000, .beds5.baths3.sqf2001-3000, .beds5.baths4.sqf2001-3000, .beds5.baths5.sqf2001-3000"
User Gravatar

OstrichProjects

Posted Jun 23 2014 17:55 UTC

Thanks to John and OstrichProjects for your solutions! I felt that the solution provided by Gaby was the easiest for me to understand and modify for other future uses.

Gaby, what is the easiest way to turn the buttons into Bootstrap3 selects so this filtering is easy to maneuver on mobile devices? Also, if I wanted to add a 4th criteria for acreage, how would I accomplish? For example, the acreage entered in the CMS for a property might be 2.1 or 3.5. The breakpoints would be .5 and below, .5 to 1, 1 to 1.5, etc. Thanks in advance. BTW your code applied to my working example is here: http://www.honorsproperties.com/multi_filter.html

User Gravatar

brianbmg

Posted Jun 24 2014 6:04 UTC

Add a reply

By posting a reply on CodersClan you agree to our Terms & Conditions