I’m creating a script where a search input field hides any items (spread throughout a few unordered lists) which don’t contain any of text in the search input field, and it is mostly working in all the ways I want it to. I have done something similar before which all worked fine.
However this time as well as hiding all the items that don’t match the input text, it also hides any parent containers if all of it’s children list items are hidden (because none of them contain the searched input text).
For some reason this time when I add the parent hiding functionality it’s hiding some items that actually match the inputted search text and should be showing still, but then it’s not hiding others items that match and I can’t figure out why.
I’ll add the basic script below to help:
let filterList = [];
let filters = document.querySelectorAll('ul.list-filters a');
// Add the search input field:
let tEl = document.querySelector('#productListLeft');
let nEl = document.createElement('input');
nEl.setAttribute('id' , 'filterSearch');
nEl.setAttribute('type' , 'text');
nEl.setAttribute('placeholder' , 'Search for filters');
tEl.prepend(nEl);
// Filter/hide items if they don't matched seached input text:
let filtersSearch = document.querySelector('#filterSearch');
for (let i = 0; i < filters.length; i++) {
let filterName = filters[i].querySelector('span:first-of-type').textContent.toLowerCase();
filterList.push(filterName);
}
console.log(filterList);
filtersSearch.addEventListener('keyup', (e) => {
let text = filtersSearch.value.toLowerCase();
for (let i = 0; i < filters.length; i++) {
if (filterList[i].indexOf(text) < 0) {
filters[i].style.display = 'none';
} else {
filters[i].style.display = 'block';
}
}
// Hiding the parent div if all children are hidden (THIS IS THE PART THAT INCONCISTENTLY BREAKS THE FUNCTIONALITY AT SOME POINT:)
let parentFilter = document.querySelectorAll(".filterSet");
let visCheck = true;
for(i = 0; i < parentFilter.length; i++) {
let children = parentFilter[i].querySelectorAll('ul.list-filters a');
for(x = 0; x < children.length; x++) {
let child = children[x];
if(child.style.display != "none") {
visCheck = true;
} else {
visCheck = false;
}
}
if (visCheck == false) {
parentFilter[i].style.display = "none";
} else if (visCheck == true) {
parentFilter[i].style.display = "block";
}
}
});
body {
margin-bottom: 200px;
}
<div id="productListLeft">
<div class="filterSet">
<h4>Filters 1</h4>
<ul class="list-filters">
<li><a href="#"><span>Foo</span></a></li>
<li><a href="#"><span>Bar</span></a></li>
<li><a href="#"><span>Ball</span></a></li>
<li><a href="#"><span>Next</span></a></li>
</ul>
</div>
<div class="filterSet">
<h4>Filters 2</h4>
<ul class="list-filters">
<li><a href="#"><span>Chair</span></a></li>
<li><a href="#"><span>Kind</span></a></li>
<li><a href="#"><span>Salt</span></a></li>
<li><a href="#"><span>Horror</span></a></li>
</ul>
</div>
<div class="filterSet">
<h4>Filters 3</h4>
<ul class="list-filters">
<li><a href="#"><span>King</span></a></li>
<li><a href="#"><span>Stagged</span></a></li>
<li><a href="#"><span>Tomatoe</span></a></li>
<li><a href="#"><span>Grilled</span></a></li>
</ul>
</div>
</div>
If you run the code snippet and enter just r
in the search input field it hides all the items that don’t have an r in them, and then also hides the parent container if all the items are hidden, which is correct. However if you just enter f
in the search input field it hides even items that have an f in them (like the first item ‘foo’), and I can’t figure out why.
I have actually got a jQuery solution (to replace the issue part of hiding the parent if the children items are all hidden) which works just as I need it too without the same issue, but I want to keep it as just JS and have struggled to translate the same code in JS (especially the $(this).find("ul.list-filters a").map(function(){
part.
Here’s the code in case it’s easier for someone to help convert it to the JS version:
$('.filterSet').map(function(){
let visCheck = true;
$(this).find("ul.list-filters a").map(function(){
if ( $(this).css("display") != "none" ) {
visCheck = false;
}
});
if ( visCheck == true ) {
$(this).css("display", "none");
} else {
$(this).css("display", "block");
}
});
Help to solve either the issue with my original JS script or replicating the jQuery version in JS would be much appreciated, thanks.
2
Answers
There is actually a lot of things that i’d like to improve in your code to make it cleaner, but I don’t want to complicate things for you. Without changing your code too much, the simple fix is to add
break
statement inside the if block like this:so why do we need that break? because if we don’t add break, even if there is a non-hidden child, it will become false again if there are other hidden child after that non-hidden child.
Here is the updated snippet:
Damzaky already provided a perfect fix for your problem (+1). In case you are interested in a slightly shorter version, here is one based on an
Array.some()
loop: