2

I try to create simple filtering with JS but I have problem with multiple select fields, they are not cooperate together, and results going crazy (sometimes nothing shows). Everything is good if I use only one select, when I try to add second one, or third one results are not correct or I got blank page.

Here is my code: https://jsfiddle.net/au6jbsL5/

And here is my filter function (I have 3 functions - one for each select group)

$('select#sort-cost').change(function() {
var filter = $(this).val()
filterList(filter);
});

function filterList(value) {
var list = $(".news-list .news-item");
$(list).fadeOut("fast");
if (value == "All") {
    $(".news-list").find("article").each(function (i) {
        $(this).delay(100).slideDown("fast");
    });
} else {
    $(".news-list").find("article[data-category*=" + value + "]").each(function (i) {
        $(this).delay(100).slideDown("fast");
    });
}
}
4
  • You have 3 functions, all called the same thing... ergo, you have 1 function Commented Oct 24, 2019 at 23:27
  • Can you explain? Commented Oct 24, 2019 at 23:32
  • You've defined a function called filterList, and told it to do X. You then define another function, also calling it filterList, this time telling it to do Y. You can not have two functions called the same thing, so your first filterList is overwritten Commented Oct 24, 2019 at 23:38
  • Yes, my mistake. But even with this fix it’s not work correctly. Results are not correct. Commented Oct 25, 2019 at 0:30

1 Answer 1

1

There's two problems here, one that you're overwriting your function, so you think you have three when you only have one, but also that you're not filtering by all the filters at once.

What you want here is not three copy-pasted functions, but instead one function that applies all three filters each time something changes.

I also changed it to use regular expressions instead of .find(), which is what allows them to be chained together and allows for a natural behavior of 'all' instead of using a bunch of if statements.

// If any of the filters change
$('select').change(function() {
    runAllFilters();
});

function runAllFilters() {
    var list = $(".news-list .news-item");
    $(list).fadeOut("fast");

    var filtered = $(".news-list article");

    // Get all filter values
    var cost = $('select#sort-cost').val();
    var city = $('select#sort-city').val();
    var age = $('select#sort-age').val();

    // Filter based on all of them 
    filtered = filtered.filter(function() {
        return RegExp(cost).test($(this).attr("data-category")) &&
            RegExp(age).test($(this).attr("data-age")) &&
            RegExp(city).test($(this).attr("data-city"));
    });

    // Show message if there are no results
    filtered.length === 0 
        ? $('.news-list').append("<p id='noresults'>No Results!</p>")
        : $('#noresults').remove()

    // Display Them
    filtered.each(function (i) {
            $(this).delay(100).slideDown("fast");
        });
};

This approach also takes a lot of the repetition out of the code.

The only thing I changed in the HTML is to change the value 'All' to just '.' so that you don't need a bunch of if statements; in RegEx "." matches everything so it works as-is.

<select name="sort-cost" id="sort-cost">
    <option value=".">All</option>
    <option value="1">Bestsellers</option>
    <option value="2">Sales</option>
</select> 

// If any of the filters change
$('select').change(function() {
	runAllFilters();
});

function runAllFilters() {
	var list = $(".news-list .news-item");
	$(list).fadeOut("fast");

	var filtered = $(".news-list article");

	// Get all filter values
	var cost = $('select#sort-cost').val();
	var city = $('select#sort-city').val();
	var age = $('select#sort-age').val();

	// Filter based on all of them 
	filtered = filtered.filter(function() {
		return RegExp(cost).test($(this).attr("data-category")) &&
			RegExp(age).test($(this).attr("data-age")) &&
			RegExp(city).test($(this).attr("data-city"));
	});

	filtered.length === 0 
		? $('.news-list').append("<p id='noresults'>No Results!</p>")
		: $('#noresults').remove()

	// Display Them
	filtered.each(function (i) {
			$(this).delay(100).slideDown("fast");
		});
};
.news-item{
  display:inline-block;
  vertical-align:top;
  width:20%;
  text-align:center;
  background: #fff;
  border:1px solid #333;
  float:left;
}  
.sort{
  display:inline-block;
  margin-right:30px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<h1>SORT NOW</h1>

<div class="sort">
COST
	<select name="sort-cost" id="sort-cost">
		<option value=".">All</option>
		<option value="1">Bestsellers</option>
		<option value="2">Sales</option>
	</select> 
</div>

<div class="sort">
AGE
<select name="sort-age" id="sort-age">
	<option value=".">All</option>
	<option value="a">3+</option>
	<option value="b">5+</option>
  <option value="c">9+</option>
</select>
</div>

<div class="sort">
CITY
<select name="sort-city" id="sort-city">
	<option value=".">All</option>
	<option value="ny">New York</option>
	<option value="la">Los Angeles</option>
  <option value="lv">Las Vegas</option>
</select>
</div>
<br><br>

<section class="news-list">
	<article class="news-item" data-category="1 2" data-age="a" data-city="la lv ny">
		<div class="thumb">
			<img src="http://placehold.it/100x100">
		</div>
		<div class="news-txt">
			<p>First one</p>
		</div>
	</article>

	<article class="news-item" data-category="1" data-age="a b" data-city="ny">
		<div class="thumb">
			<img src="http://placehold.it/100x100">
		</div>
		<div class="news-txt">
			<p>Second one</p>
		</div>
	</article>

	<article class="news-item" data-category="2" data-age="a b" data-city="la ny">
		<div class="thumb">
			<img src="http://placehold.it/100x100">
		</div>
		<div class="news-txt">
			<p>Third one</p>
		</div>
	</article>

	<article class="news-item" data-category="1 2" data-age="c" data-city="la lv ny">
		<div class="thumb">
			<img src="http://placehold.it/100x100">
		</div>
		<div class="news-txt">
			<p>Fifth</p>
		</div>
	</article>
</section>

Sign up to request clarification or add additional context in comments.

4 Comments

OK, I will try this. But I have another problem. How to show display "No results!" if nothing matches?
You can just check the length of the array returned from the filter step, and if it's zero add your message to the DOM.
@Mike the answer now includes that message—just a quick example of how you would implement it
How can you combine this with a multi-select drop down to view more than one option from the same drop down? I was looking at implementing this (harvesthq.github.io/chosen) – I sort of got it working by adding a comma between data-attributes ~ i.e. (data-category="1,2,3”) however I discovered that the selection does not always properly filter unless the selection are made in sequential order. I suspect the RegExp is matching elements to closely? ~ here is an example setup if you want to click around to see what I mean: codepen.io/jinch/pen/poRpgzV?editors=0010

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.