Filtering script for products by category

Posted on

Problem

I built a simple product filtering feature that works at least in the browsers and platforms that I have tried using it on, but I am unsure about a couple of aspects of it. I built this on a Bigcommerce site but I think my questions could still apply broadly (some):

  1. Is this a solid approach?  Conventional? If not, why, and how could/should I approach it differently?   

  2. Should I expect to have any problems with browser compatibility?  If so, would they be different problems than any problems I might experience if I was using the stock Bigcommerce faceted search option, or even some other random search feature from another site?

Assume an out of the box faceted search option is not an available. jQuery is loaded twice in HTMLhead.html (head template) as 1.7.2 and 2.2.2

The following example is a simplified version of what I created.  In this example there are only 4 products and 2 categories present.  We can also assume that no products belong in more than one category:

HTML – (Two checkboxes, one for each category)

<fieldset>
    <div class="col-md-6 test-one"><label><input id="example-category-1" type="checkbox" value="first-checkbox" />CHOOSE CATEGORY 1</label></div>
    <div class="col-md-6 test-one"><label><input id="example-category-2" type="checkbox" value="second-checkbox" />CHOOSE CATEGORY 2</label></div>
</fieldset>

HTMLfiddle with expanded li showing the structure of a random example product.

JS:

Page loads, all products present, no checkboxes clicked. When a checkbox is clicked for the first time, all products are hidden (rather, their parent lis are)

    var test = true;
    $(".test-one").change(function() {
        if(test){           
            $('div[data-product]').parents("li").hide();
            //hide every product after first click on any checkbox 
            
            if( $('#example-category-1').is(':checked') ) {
            //check for checkbox activation and show products if true
                $('div[data-product="1802"]').parents("li").show();
                $('div[data-product="1781"]').parents("li").show();
            }
            if( $('#category-2').is(':checked') ) {
            //check for checkbox activation and show products if true
                $('div[data-product="1348"]').parents("li").show();
                $('div[data-product="1347"]').parents("li").show();
            }
            test =false;
            //set to false so we don't enter this if statement again
        }
    });

For the rest of the time, this is what is handling the filtering (would like to exclude the following code from the first click, but seems to work okay as is):

$('#example-category-1').click(function() {
        if( $(this).is(':checked') ) {
            $('div[data-product="1802"]').parents("li").show();
            $('div[data-product="1781"]').parents("li").show();
        } else {
            $('div[data-product="1802"]').parents("li").hide();
            $('div[data-product="1781"]').parents("li").hide();
        }
    });
    $('#example-category-2').click(function() {
        if( $(this).is(':checked') ) {
            $('div[data-product="1348"]').parents("li").show();
            $('div[data-product="1347"]').parents("li").show();
        } else {
            $('div[data-product="1348"]').parents("li").hide();
            $('div[data-product="1347"]').parents("li").hide();
        }
    });

Final note: I was told that I can manipulate the product object to achieve goals like this one – I kind of know in theory what the product object is, but I don’t know how to access it / act on it – any input, test suggestions, or criticism would be greatly appreciated.

Solution

I don’t see how this is scalable if you have to hard code product id’s into your jQuery code and duplicate handlers for every category you may want to add.

I also see no need to have separate logic for “first time” execution vs. every other time.

I would consider putting a data- attribute on each checkbox that can indicate the target category the checkbox filter is to work against. And then put a data- attribute on each product to indicate what category it belongs to.

So let’s assume your products are like this:

<div class="product" data-category="1">...</div>
<div class="product" data-category="2">...</div>

And your category checkboxes are like:

<input class="category_filter" data-category-target="1" type="checkbox" value="first-checkbox" />
<input class="category_filter" data-category-target="2" type="checkbox" value="second-checkbox" />

Then your jQuery might be simplified down to something like:

// cache references to DOM elements to eliminate need to requery
var $categoryFilters = $('.category_filter');
var $allProducts = $('.product');
// build out selectors for each category
var productsByCategory = {};
$categoryFilters.each(function()) {
     var category = $(this).data('category-target');
     productsByCategory[category] = $allProducts.filter('[data-category=' + category + ']');
});

// now attach change handler to filters
$categoryFilters.on('change', function() {
    $allProducts.hide();
    $categoryFilters.each(function() {
        var target = $(this).data('category-target');      
        if($(this).is(':checked')) {
            productsByCategory[target].show();
        }
    }
});

Why are you loading two versions of jQuery? If this is absolutely unavoidable, you should be aliasing these versions appropriately (i.e. to something other than $) so that you can write code against whichever version is pertinent to that section of code.


Anytime you find yourself duplicating code for cases like you have with #example-category-*, this should be an immediate red flag to you that have established a coding antipattern. For this specific example, what you really have is class level behavior (all of the category checkboxes exhibit similar behavior in this case). That typically mean you should be writing code against the class of elements, not against individual unique id’s.

Leave a Reply

Your email address will not be published. Required fields are marked *