I'm working on filtering a directory that's fed through a component created by our CMS, which I have no control over. The tags for each directory member that I'm trying to filter are inside divs with class of d-none. The script I'm using works except it only displays the items I want filtered when they are the first div inside the card. I'm trying to figure out how to filter a tag if it's not the first item within a card.
For instance, in the following code the filter buttons work until you press the All employees button. Then only Christina's name is displayed because "Employee" is the first tag in her card.
<h1>Employees</h1>
<p>
<button onclick="queryButton('FULL TIME')">Full Time</button>
<button onclick="queryButton('PART TIME')">Part Time</button>
<button onclick="queryButton('SEASONAL')">Seasonal</button>
<button onclick="queryButton('EMPLOYEE')">All Employees</button>
</p>
<div class="cpFeed">
<div class="card-news">
<div style="display: none;" class="d-none">Part Time</div>
<div style="display: none;" class="d-none">Employee</div>
<div>Adele</div>
</div>
<div class="card-news">
<div style="display: none;" class="d-none">Full Time</div>
<div style="display: none;" class="d-none">Employee</div>
<div>Agnes</div>
</div>
<div class="card-news">
<div style="display: none;" class="d-none">Seasonal</div>
<div style="display: none;" class="d-none">Employee</div>
<div>Billy</div>
</div>
<div class="card-news">
<div style="display: none;" class="d-none">Part Time</div>
<div style="display: none;" class="d-none">Employee</div>
<div>Bob</div>
</div>
<div class="card-news">
<div style="display: none;" class="d-none">Seasonal</div>
<div style="display: none;" class="d-none">Employee</div>
<div>Calvin</div>
</div>
<div class="card-news" class="d-none">
<div style="display: none;" class="d-none">Employee</div>
<div style="display: none;" class="d-none">Seasonal</div>
<div>Christina</div>
</div>
<div class="card-news" class="d-none">
<div style="display: none;" class="d-none">Part Time</div>
<div style="display: none;" class="d-none">Employee</div>
<div>Cindy</div>
</div>
</div>
<script>
function queryButton(queryFilter) {
var filter, feedDiv, cardDiv, unnamedDiv, i, txtValue;
filter = queryFilter;
feedDiv = document.querySelector("div.cpFeed");
cardDiv = feedDiv.querySelectorAll("div.card-news");
for (i = 0; i < cardDiv.length; i++) {
unnamedDiv = cardDiv[i].querySelector(".d-none");
[0];
txtValue = unnamedDiv.textContent || div.div.innerHTML;
if (txtValue.toUpperCase().indexOf(filter) > -1) {
cardDiv[i].style.display = "";
} else {
cardDiv[i].style.display = "none";
}
}
}
</script>
I tried changing the querySelector to querySelectorAll in the following line. The
unnamedDiv = cardDiv[i].querySelectorAll(".d-none");
This causes the buttons' functions to stop working. I get the following error in the console:
Uncaught ReferenceError: div is not defined
at queryButton (example.html:56:44)
at HTMLButtonElement.onclick (example.html:5:45)
Is there something I'm missing?
I can see some issues with your code, so here's an explanation.
First, here's my own way of writing your <script>
to hopefully make it easier to understand. You don't have to structure it this way if you don't want to. This block changes nothing of what your code does.
function queryButton(filter) {
let feedDiv = document.querySelector("div.cpFeed"), // Get's the first <div> with a class of cpFeed
cardDiv = feedDiv.querySelectorAll("div.card-news"), // Get's all <div> children of feedDiv with a class of card-news
unnamedDiv, // pre-sets to a variable to undefined
i, // pre-sets to a variable to undefined
txtValue // pre-sets to a variable to undefined
for (i = 0; i < cardDiv.length; i++) { // Sets a previously undefined variable to 0, to be looped through
unnamedDiv = cardDiv[i].querySelector(".d-none") // Gets first of any element, child of the indexed (i) cardDiv, with a class d-none
[0] // Does nothing
txtValue = unnamedDiv.textContent || div.div.innerHTML // returns unnamedDiv.textContent if unnamedDiv.textContent is not undefined. Otherwise, returns div.div.innerHTML, which is not assigned, so it will error out.
// Returned error from div.div.innerHTML:
// Uncaught ReferenceError: div is not defined
// at queryButton (example.html:56:44)
// at HTMLButtonElement.onclick (example.html:5:45)
if (txtValue.toUpperCase().indexOf(filter) > -1) { // Checks whether an uppercase string's index of the filter param is greater than -1
cardDiv[i].style.display = "" // Sets the indexed (i) cardDiv's display to empty / it's default value.
} else {
cardDiv[i].style.display = "none" // Sets the indexed (i) cardDiv's display to "none"
}
}
}
Hopefully just from the comments above, you'll be able to work out your errors, in your own way.
Of course, if you want to just have a working example, here's me actually making the script work.
function queryButton(filter) {
// Switched to const since these variable will not be changed
const feedDiv = document.querySelector(".cpFeed"), // Get's the first of any element with a class of cpFeed
cardDivs = feedDiv.querySelectorAll(".card-news") // Get's all of any element, children of feedDiv, with a class of card-news
// unnamedDiv, – Removed to be assigned per cardDiv later
// i, – Removed because for loop is swtiched to forEach()
// txtValue – Removed to be assigned per cardDiv later
// New forEach() instead of for loop
cardDivs?.forEach((cardDiv) => {
// Adding variables as const since they do not change
const unnamedDivs = cardDiv.querySelectorAll(".d-none") // Gets all of any element, child of the the current cardDiv, with a class d-none
let display = "none" // preset to none
unnamedDivs?.forEach((unnamedDiv) => {
// Checks whether display has already succeeded in one of the unnamedDivs
if (display !== "none") display = unnamedDiv.textContent.toUpperCase() === filter ? "" : "none"
// Always returns something
cardDiv.style.display = display
})
})
}
As you can see, there are a few things I did here that simplify the code, making it more readable, and add some safety checks, so we avoid as many errors as possible.
I also noticed a couple errors in your HTML:
<!-- Two class attributes will break things -->
<!-- BEFORE: <div class="card-news" class="d-none"> -->
<div class="card-news">
<div style="display: none;" class="d-none">Employee</div>
<div style="display: none;" class="d-none">Seasonal</div>
<div>Christina</div>
</div>
<!-- Two class attributes will break things -->
<!-- BEFORE: <div class="card-news" class="d-none"> -->
<div class="card-news">
<div style="display: none;" class="d-none">Part Time</div>
<div style="display: none;" class="d-none">Employee</div>
<div>Cindy</div>
</div>
I also made a CodePen so you can see it in action: Go to CodePen