Search code examples
javascriptjquerylogical-operatorsmixitup

Using both 'and' and 'or' logic with dropdowns and MixItUp


I have successfully implemented the fabulous MixItUp code for some dropdowns/select boxes. I've tried following the Advanced Tutorials but the one for using both logics uses checkboxes, and I can't figure out how to make it work for select boxes. Also, there is just one group of select boxes that needs the OR logic, all the rest needs AND.

The logic is as follows:

school subject AND minimum grade AND maximum grade AND (first language OR second language).

The filter controls are as follows:

<form class="controls form-horizontal" id=Filters>
   <fieldset class=MixItUpfilters>
      <legend>Filters</legend>
      <div class=form-group>
         <label for=selectSubject class="col-md-3 control-label">with the school subject of</label>
         <div class=col-md-9>
            <fieldset>
               <select id=selectSubject class="form-control input-sm">
                  <option value="">-- select a school subject --</option>
                  <option value=".subject-1">Subject 1</option>
                  <option value=".subject-2">Subject 2</option>
                  <option value=".subject-3">Subject 3</option>
               </select>
            </fieldset>
         </div>
      </div>
      <div class=form-group>
         <label for=selectMinGrade class="col-md-3 control-label">for grades </label>
         <div class=col-md-9>
            <fieldset class=inline>
               <select id=selectMinGrade class="form-control input-sm">
                  <option value="">-- select a minimum grade --</option>
                  <option value=".min-grade-01">Grade 1</option>
                  <option value=".min-grade-02">Grade 2</option>
                  <option value=".min-grade-03">Grade 3</option>
               </select>
            </fieldset>
            <label>to </label>
            <fieldset class=inline>
               <select id=selectMaxGrade class="form-control input-sm">
                  <option value="">-- select a maximum grade --</option>
                  <option value=".max-grade-01">Grade 1</option>
                  <option value=".max-grade-02">Grade 2</option>
                  <option value=".max-grade-03">Grade 3</option>
               </select>
            </fieldset>
         </div>
      </div>
      <div class=form-group>
         <label for=selectFirstLanguage class="col-md-3 control-label">in First language</label>
         <div class=col-md-9>
            <fieldset class=inline>
               <select id=selectFirstLanguage class="form-control input-sm">
                  <option value="">-- select a language --</option>
                  <option value=".first-language-english">English</option>
                  <option value=".first-language-afrikaans">Afrikaans</option>
                  <option value=".first-language-zulu">Zulu</option>
               </select>
            </fieldset>
            <label>or second language</label>
            <fieldset class=inline>
               <select id=selectSecondLanguage class="form-control input-sm">
                  <option value="">-- select a language --</option>
                  <option value=".second-language-english">English</option>
                  <option value=".second-language-afrikaans">Afrikaans</option>
                  <option value=".second-language-zulu">Zulu</option>
               </select>
            </fieldset>
         </div>
      </div>
   </fieldset>
</form>

And the javascript is (note: I use jQuery instead of $ because this is a WordPress site):

<script>
  // To keep our code clean and modular, all custom functionality will be contained inside a single object literal called "dropdownFilter".
  var dropdownFilter = {

      // Declare any variables we will need as properties of the object
      jQueryfilters: null,
      jQueryreset: null,
      groups: [],
      outputArray: [],
      outputString: '',

      // The "init" method will run on document ready and cache any jQuery objects we will need.
      init: function(){
          var self = this; 
          /*  As a best practice, in each method we will assign "this" to the variable "self" 
              so that it remains scope-agnostic. We will use it to refer to the parent "dropdownFilter" 
              object so that we can share methods and properties between all parts of the object.
          */ 

          self.jQueryfilters = jQuery('#Filters');
          self.jQueryreset = jQuery('#Reset');
          self.jQuerycontainer = jQuery('#Container');

          self.jQueryfilters.find('fieldset').each(function(){
            self.groups.push({
              jQuerydropdown: jQuery(this).find('select'),
              active: ''
            });
          });

          self.bindHandlers();
        },

      // The "bindHandlers" method will listen for whenever a select is changed. 
      bindHandlers: function(){
          var self = this;

          // Handle select change
          self.jQueryfilters.on('change', 'select', function(e){
              e.preventDefault();
              self.parseFilters();
          });

          // Handle reset click
          self.jQueryreset.on('click', function(e){
            e.preventDefault();
            self.jQueryfilters.find('select').val('');
            self.parseFilters();
          });
      },

      // The parseFilters method pulls the value of each active select option
      parseFilters: function(){
          var self = this;

          // loop through each filter group and grap the value from each one.
          for(var i = 0, group; group = self.groups[i]; i++){
              group.active = group.jQuerydropdown.val();
              }
          self.concatenate();
      },

      // The "concatenate" method will crawl through each group, concatenating filters as desired:
      concatenate: function(){
          var self = this;
          self.outputString = ''; // Reset output string
          for(var i = 0, group; group = self.groups[i]; i++){
          self.outputString += group.active;
          }

      // If the output string is empty, show all rather than none:
      !self.outputString.length && (self.outputString = 'all'); 
      console.log(self.outputString); 
      // ^ we can check the console here to take a look at the filter string that is produced

      // Send the output string to MixItUp via the 'filter' method:
        if(self.jQuerycontainer.mixItUp('isLoaded')){
          self.jQuerycontainer.mixItUp('filter', self.outputString);
        }
    }
  };

  // On document ready, initialise our code.
  jQuery(function(){
    // Initialize dropdownFilter code
    dropdownFilter.init();

    // Instantiate MixItUp
    jQuery('#Container').mixItUp({
      controls: {
          enable: false // as we are interacting via the API, we can disable default controls to increase performance
      },
      callbacks: {
        onMixFail: function(){
          console.log('No items were found matching the selected filters.');                    
        }
      },
      layout: {
              containerClassFail: 'none-found'
      }                
    });    
  });
</script>

Solution

  • From the mixitup documentation there doesn't seem to be a configuration mechanism for 'complicated' logic. You can either 'and' or 'or' everything together. However, the docs do at least tell us the syntax for using this logic, which is essentially CSS selector syntax, i.e. .class1.class2 is class1 AND class2 and .class1, .class2 is class1 OR class2. Without being able to use the configuration object, you will have to write your own concatenation method, something like this

    concatenate: function(){
      var self = this;
      self.outputString = ''; // Reset output string
      var anded = self.groups[0].active+self.groups[1].active+self.groups[2].active
      self.outputString = anded+self.groups[3].active+', '+anded+self.groups[4].active
      // outputString will now look like
      // subject.min-grade.max-grade.first-language, subject.min-grade.max-grade.second-language
    
      // If the output string is empty, show all rather than none:
      !self.outputString.length && (self.outputString = 'all'); 
      console.log(self.outputString); 
    
      // Send the output string to MixItUp via the 'filter' method:
      if(self.jQuerycontainer.mixItUp('isLoaded')){
        self.jQuerycontainer.mixItUp('filter', self.outputString);
      }
    }
    

    This assumes your group order matches the logic string you mentioned above your code, where the first select is subject, the second minimum grade, etc..