Reputation: 638
I have two options that I am trying to filter between now, but will be adding in a third and maybe a fourth later on (for example: right now I am filtering between prices and reviews but will want to add more prices to the list and also another filter category like "rating" which would consist of 3, 4, or 5 stars).
Using the logic that I have listed below works fine, but I feel it will get really long and complicated (and unnecessary) as I go. I know there is a way to refactor the code I'm just wondering what the best way to go about it would be? HTML:
<select class="deals">
<option value="deals-all">All deals</option>
<option value="50">$50</option>
<option value="25">$25</option>
</select>
<select class="reviews">
<option value="reviews-all">All reviews</option>
<option value="reviews-positive">Positive reviews</option>
<option value="reviews-negative">Negative reviews</option>
</select>
jQuery
$('.reviews, .deals').change(function() {
var reviewsVal = $('.reviews :selected').val();
var dealsVal = $('.deals :selected').val();
if((reviewsVal == 'reviews-all') && (dealsVal == 'deals-all')) {
$('.review-positive').show();
$('.review-negative').show();
$('.deals-25').show();
$('.deals-50').show();
}
else if((dealsVal == '50') && (reviewsVal == 'reviews-positive')) {
$('.review-negative.deals-50').hide();
$('.review-positive.deals-50').show();
$('.review-negative.deals-25').hide();
$('.review-positive.deals-25').hide();
}
else if((dealsVal == '50') && (reviewsVal == 'reviews-negative')) {
$('.review-negative.deals-50').show();
$('.review-positive.deals-50').hide();
$('.review-negative.deals-25').hide();
$('.review-positive.deals-25').hide();
}
else if((dealsVal == '25') && (reviewsVal == 'reviews-positive')) {
$('.review-negative.deals-50').hide();
$('.review-positive.deals-50').hide();
$('.review-negative.deals-25').hide();
$('.review-positive.deals-25').show();
}
else if((dealsVal == '25') && (reviewsVal == 'reviews-negative')) {
$('.review-negative.deals-50').hide();
$('.review-positive.deals-50').hide();
$('.review-negative.deals-25').show();
$('.review-positive.deals-25').hide();
}
else if((dealsVal == 'deals-all') && (reviewsVal == 'reviews-positive')) {
$('.review-negative.deals-50').hide();
$('.review-positive.deals-50').show();
$('.review-negative.deals-25').hide();
$('.review-positive.deals-25').show();
}
else if((dealsVal == 'deals-all') && (reviewsVal == 'reviews-negative')) {
$('.review-negative.deals-50').show();
$('.review-positive.deals-50').hide();
$('.review-negative.deals-25').show();
$('.review-positive.deals-25').hide();
}
else if((dealsVal == '50') && (reviewsVal == 'reviews-all')) {
$('.review-negative.deals-50').show();
$('.review-positive.deals-50').show();
$('.review-negative.deals-25').hide();
$('.review-positive.deals-25').hide();
}
else if((dealsVal == '25') && (reviewsVal == 'reviews-all')) {
$('.review-negative.deals-50').hide();
$('.review-positive.deals-50').hide();
$('.review-negative.deals-25').show();
$('.review-positive.deals-25').show();
}
else {
$('.review-positive').show();
$('.review-negative').show();
$('.deals-25').show();
$('.deals-50').show();
}
});
$('.reviews-positive').click(function() {
$('.review-negative').hide();
$('.review-positive').show();
});
$('.reviews-negative').click(function() {
$('.review-positive').hide();
$('.review-negative').show();
});
});
Hopefully you can see what I am going for, thanks for any input.
*EDIT: jsfiddle: http://jsfiddle.net/TXywp/1/
Upvotes: 1
Views: 4684
Reputation: 707876
I can think of two different approaches to simplify the code and make it easier to maintain and grow:
You can algorithmically derive what items should be hidden or shown based on the conditions.
You can create a table of conditions and actions so that adding a new condition and action is just adding a new item to the table.
Change your HTML to make it self-describing for what it should display.
Here's what the table driven approach looks like:
$('.reviews, .deals').change(function() {
var allDeals = '.review-negative.deals-50, .review-positive.deals-50, .review-positive.deals-25, .review-negative.deals-25';
var table = [
{rv: 'reviews-all', dv: 'deals-all', show: '.review-positive, .review-negative, .deals-25, .deals-50'},
{rv: 'reviews-positive', dv: '50', show: '.review-positive.deals-50'},
{rv: 'reviews-negative', dv: '50', show: '.review-negative.deals-50'},
{rv: 'reviews-positive', dv: '25', show: '.review-positive.deals-25'}
{rv: 'reviews-negative', dv: '25', show: '.review-negative.deals-25'},
{rv: 'reviews-positive', dv: 'deals-all', show: '.review-positive.deals-50, .review-positive.deals-25'},
{rv: 'reviews-negative', dv: 'deals-all', show: '.review-negative.deals-50, .review-negative.deals-25'},
{rv: 'reviews-all', dv: '50', show: '.review-negative.deals-50, .review-positive.deals-50'},
{rv: 'reviews-all', dv: '25', show: '.review-negative.deals-25, .review-positive.deals-25'}
];
var reviewsVal = $('.reviews :selected').val();
var dealsVal = $('.deals :selected').val();
$(allDeals).hide();
var item, found = false;
for (var i = 0, len = table.length; i < len; i++) {
item = table[i];
if (dealsVal == item.dv && reviewsVal == item.rv) {
$(item.show).show();
found = true;
break;
}
}
if (!found) {
$('.review-positive, .review-negative, .deals-25, .deals-50').show();
}
});
And, here's an algorithmic way that lets you add a new deal value by just adding one new entry to an array. With only two deals, this is probably more work than the table driven approach, but if you had 4 or more deal levels, this would a lot simpler to maintain.
$('.reviews, .deals').change(function() {
var deals = ['25', '50'];
function addAllDeals(base, prefix) {
for (var i = 0; i < deals.length; i++) {
if (base) base += ", ";
base += prefix + deals[i];
}
return(base);
}
function addSingleDeal(prefixes, deal) {
var sel = [];
for (var i = 0; i < prefixes.length; i++) {
sel.push(prefixes[i] + deal);
}
return(sel.join(", ");
}
var reviewsVal = $('.reviews :selected').val();
var dealsVal = $('.deals :selected').val();
var itemsToShow = "";
// hide everything to start
var initialHide = addAllDeals("", ".review-positive.deals-");
initialHide = addAllDeals(initialHide, ".review-negative.deals-");
$(initialHide).hide();
if (reviewsVal == 'reviews-all') {
if (dealsVal == 'deals-all') {
itemsToShow = addAllDeals(".review-positive, .review-negative", ".deals-");
} else {
itemsToShow = addSingleDeal([".review-negative.deals-", ".review-negative.deals-"], dealsVal);
}
} else if (reviewsVal == 'reviews-positive') {
itemsToShow = '.review-positive.deals-' + dealsVal;
} else if (reviewVal == 'reviews-negative') {
itemsToShow = '.review-negative.deals-' + dealsVal;
} else {
itemsToShow = addAllDeals(".review-positive, .review-negative", ".deals-");
}
});
If you can change your HTML (as in my third option above) to this:
<select class="deals">
<option value="deals-all" data-base=".review-positive, .review-negative">All deals</option>
<option value="50" data-base=".deals-50">$50</option>
<option value="25" data-base=".deals-25">$25</option>
</select>
<select class="reviews">
<option value="reviews-all" data-filter="">All reviews</option>
<option value="reviews-positive" data-filter=".review-positive">Positive reviews</option>
<option value="reviews-negative" data-filter=".review-negative">Negative reviews</option>
</select>
Then, you can just use this simple javascript:
$('.reviews, .deals').change(function() {
var dealsBase = $('.deals :selected').data("base");
var reviewsFilter = $('.reviews :selected').data("filter");
// hide all
$(".review-negative, .review-positive").hide();
// show the desired ones
var base = $(dealsBase);
if (reviewsFilter) {
base = base.filter(reviewsFilter);
}
base.show();
});
Caveat: Since you did not provide a working jsFiddle example of your code/HTML to try this with, this code has not been run or checked for typing errors. But, hopefully you get the idea of the two approaches.
Upvotes: 2
Reputation: 3008
To get a dynamic result a standardized naming is the best way to go. You start by standardizing the criteria selectors, giving them a general class and differentiating with id properties. Following a naming convetion for values is also recommended. This would produce something in the lines of
<select id="deal" class="criteriaSelector">
<option value="all">All deals</option>
<option value="50">$50</option>
<option value="25">$25</option>
</select>
<select id="review" class="criteriaSelector">
<option value="all">All reviews</option>
<option value="positive">Positive reviews</option>
<option value="negative">Negative reviews</option>
</select>
Then you give all the items you want to filter a generic class besides the filtering classes. I will use .item as an example. You will also want to be able to construct the class from selected value and criteria name so using the .{id}-{value} system will work perfectly. This will leave us with something in the lines of
<div class="item review-positive deal-25"></div>
<div class="item review-negative deal-50"></div>
This setup will allow us to build dynamic and extensible code:
$('.criteriaSelector').change(function() {
// Initialize criteria string
var criteria = '';
// Set value for all selector
var showAll = true;
// Iterate over all criteriaSelectors
$('.criteriaSelector').each(function(){
// Get value
var val = $(this).children(':selected').val();
// Check if this limits our results
if(val !== 'all'){
// Append selector to criteria
criteria += '.' + $(this).attr('id') + '-' + val;
// We don't want to show all results anymore
showAll = false;
}
});
// Check if results are limited somehow
if(showAll){
// No criterias were set so show all
$('.item').show();
} else {
// Hide all items
$('.item').hide();
// Show the ones that were selected
$(criteria).show();
}
});
Now adding a new criteria selector won't require changes in this piece of code as long as naming conventions are followed.
Upvotes: 1