Blog (my web musings)

Find out what's interesting me, get tips, advice, and code gems that don't fit elsewhere.


Search Blog


Back in September last year, I wrote about making a content filter where items are filtered by class. A big limitation to this script is that it uses radio buttons to make selections, and as we all know, only one (group) radio button can be selected at any time.

I decided to revisit the content filter, adding "refine" functionality, so that multi-category selections can be made. A welcome refinement, and much more usable in the real world for sure.

First I should draw your attention to my revised single-category filter script. I didn't elaborate on the changes to the JavaScript before, but I'll briefly cover them here because a similar logic will be used on the latest script version. In fact, it is this progression of logic that lead me to develop the new multi-category selections filter.

After the inital setup of a few helper functions to add and delete classes, the part of the script that performs the filter function looks like this;

document.getElementById('filter-categories').onclick = function(evt) { 
    evt = evt || window.event;
    var elem = evt.target || evt.srcElement;
    var filter = (elem.id == 'filter-all') ? '' : '.'+elem.id;
    var mask = document.querySelectorAll('#filter-mask');
    addClassAll(mask, 'filter-mask');
    setTimeout(function() {
       delClassAll(document.querySelectorAll('.filter-item'), 'selected');
       addClassAll(document.querySelectorAll('.filter-wrap'), 'filtered');
       addClassAll(document.querySelectorAll('.filter-item'+filter), 'selected');
        }, 500);
    setTimeout(function() { delClassAll(mask, 'filter-mask'); }, 1000);
    }

The line in red identifies the id of the selected filter-category radio, while the line in green takes this id and assembles a CSS selector .filter-item.blue, in order to apply the ".selected" class to those filter-items. Once the class is applied via JS, the CSS kicks in to reveal the selected items;

.filtered .filter-item { display:none }
.filtered .filter-item.selected { display:block } /* selected class applied via js */

It is this CSS selector that started my brain cogs a-turning - if I could select multiple categories (using checkboxes instead of radios), and chain their selectors thusly .filter-item.blue.red.green, I could target them with CSS in the same way. So from that spawned...

JavaScript & CSS3 Content Filter + Refine with Multi-Category Select:

Demo

Filtering Options

Now, this new demo has an inclusive and exclusive filtering option;

  • The inclusive filter shows items that have all of the selected category classes (works in all browsers but no fade effect in IE8/9).
  • The exclusive filter shows items that have none of the selected category classes (works in all modern browsers (IE9+) owing to the use of the :not() CSS selector, but has no fade effect in IE9).

Inclusive Filtering

It is the inclusive part of the filter JS that uses the chained selector logic mentioned earlier. The idea here is that I can add the selected checkbox filter ids to an array;

for (var i = 1; i < inputs.length; ++i) { // loop through inputs but ignore [0] - #filter-all 
    if (inputs[i].checked) { filters.push(inputs[i].id); } // add checked inputs to filters array
    }

And then use this array, along with the join() method, to build a CSS selector;

addClassAll(document.querySelectorAll(id+' .filter-item.'+filters.join('.')), 'selected');} // build css selector from filters array

Here's the resulting CSS selector #demo .filter-item.mild.veg - it is targetting all the filter-items that have both a ".mild" and ".veg" class, and the addClassAll() function is then giving them a ".selected" class. The CSS takes over at this point, in much the same way as the earlier incarnation of the script, to reveal those selected item;

.filtered-inclusive .filter-item, .filtered-exclusive .filter-item { display:none } /* filtered-inclusive/filtered-exclusive class applied via js */
.filtered-inclusive .filter-item.selected, .filtered-exclusive .filter-item:not(.selected) { display:block } /* selected class applied via js */

Exclusive Filtering

The exclusive filter works slightly differently. It loops through the selected checkbox filter id array, and applies the ".selected" class to all the items individually, resulting in a single filter-category selector for each selected filter-item #demo .filter-item.veg;

for (var i = 0; i < filters.length; ++i) { 
    addClassAll(document.querySelectorAll(id+' .filter-item.'+filters[i]), 'selected');
    }

The CSS for the exclusive filter is also different - it uses the :not() selector to reveal filter-items that do not have the ".selected" class applied;

.filtered-inclusive .filter-item, .filtered-exclusive .filter-item { display:none } /* filtered-inclusive/filtered-exclusive class applied via js */
.filtered-inclusive .filter-item.selected, .filtered-exclusive .filter-item:not(.selected) { display:block } /* selected class applied via js */

Compatibility

The CSS :not() selector is unsupported in IE8, so please bear in mind that the exclusive filter option will not work in that browser. The inclusive filter uses fully supported selectors though, so will work fine in IE8.

Last thing - the new content filter script been written as a function so can be used multiple times on a page ;)

I hope you like the demo - maybe it will find it's way into one of your projects?