I'm struggling to get this filter to work properly.
I have it set to filter and display only what's searched, based on the card titles (h5). It's filtering the unwanted titles out, but not the rest of the card.
To better explain, there's a demo here - JS Element Filter
Here's the Code:
function myFunction() {
var input, filter, card, h5, a, i;
input = document.getElementById("myFilter");
filter = input.value.toUpperCase();
card = document.getElementById("myItems");
h5 = card.getElementsByTagName("h5");
for (i = 0; i < h5.length; i++) {
a = h5[i].getElementsByTagName("a")[0];
if (a.innerHTML.toUpperCase().indexOf(filter) > -1) {
h5[i].style.display = "";
} else {
h5[i].style.display = "none";
}
}
}
.container {
padding: 10px;
}
ul li {
list-style: none;
}
<link rel="stylesheet" href=".0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<div class="container">
<div class="row">
<div class="col-sm-12 mb-3">
<input type="text" id="myFilter" class="form-control" onkeyup="myFunction()" placeholder="Search for names..">
</div>
</div>
<div class="row" id="myItems">
<div class="col-sm-12 mb-3">
<div class="card">
<div class="card-body">
<h5 class="card-title"><a href="#">Card One</a></h5>
<h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
<p class="card-text">Some text.</p>
</div>
</div>
<div class="card">
<div class="card-body">
<h5 class="card-title"><a href="#">Card Two</a></h5>
<h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
<p class="card-text">Some text.</p>
</div>
</div>
<div class="card">
<div class="card-body">
<h5 class="card-title"><a href="#">Card Three</a></h5>
<h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
<p class="card-text">Some text.</p>
</div>
</div>
</div>
</div>
</div>
I'm struggling to get this filter to work properly.
I have it set to filter and display only what's searched, based on the card titles (h5). It's filtering the unwanted titles out, but not the rest of the card.
To better explain, there's a demo here - JS Element Filter
Here's the Code:
function myFunction() {
var input, filter, card, h5, a, i;
input = document.getElementById("myFilter");
filter = input.value.toUpperCase();
card = document.getElementById("myItems");
h5 = card.getElementsByTagName("h5");
for (i = 0; i < h5.length; i++) {
a = h5[i].getElementsByTagName("a")[0];
if (a.innerHTML.toUpperCase().indexOf(filter) > -1) {
h5[i].style.display = "";
} else {
h5[i].style.display = "none";
}
}
}
.container {
padding: 10px;
}
ul li {
list-style: none;
}
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn./bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<div class="container">
<div class="row">
<div class="col-sm-12 mb-3">
<input type="text" id="myFilter" class="form-control" onkeyup="myFunction()" placeholder="Search for names..">
</div>
</div>
<div class="row" id="myItems">
<div class="col-sm-12 mb-3">
<div class="card">
<div class="card-body">
<h5 class="card-title"><a href="#">Card One</a></h5>
<h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
<p class="card-text">Some text.</p>
</div>
</div>
<div class="card">
<div class="card-body">
<h5 class="card-title"><a href="#">Card Two</a></h5>
<h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
<p class="card-text">Some text.</p>
</div>
</div>
<div class="card">
<div class="card-body">
<h5 class="card-title"><a href="#">Card Three</a></h5>
<h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
<p class="card-text">Some text.</p>
</div>
</div>
</div>
</div>
</div>
Share
Improve this question
edited Aug 3, 2018 at 14:21
Tschallacka
28.8k15 gold badges98 silver badges139 bronze badges
asked Aug 3, 2018 at 14:18
MillhornMillhorn
3,1767 gold badges45 silver badges92 bronze badges
1
- 1 you are hiding only the anchor tag. you need to hide the whole card block. – Kaushik Commented Aug 3, 2018 at 14:30
4 Answers
Reset to default 2Cause
You are only hiding the title (h5.card-title
) and not the whole card (div.card
)
Solution
First get a reference to the whole card. Then hide that element instead of just the title.
Implementation A
A quick and dirty solution to get a reference to the whole card would be to access it via the parentElement
property. Since the parent of your <h5>
is the card-body
and it's parent is the entire card you acces it via h5[i].parentElement.parentElement
.
So change h5[i].style.display
to h5[i].parentElement.parentElement.style.display
like this:
function myFunction() {
var input, filter, card, h5, a, i;
input = document.getElementById("myFilter");
filter = input.value.toUpperCase();
card = document.getElementById("myItems");
h5 = card.getElementsByTagName("h5");
for (i = 0; i < h5.length; i++) {
a = h5[i].getElementsByTagName("a")[0];
if (a.innerHTML.toUpperCase().indexOf(filter) > -1) {
h5[i].parentElement.parentElement.style.display = "";
} else {
h5[i].parentElement.parentElement.style.display = "none";
}
}
}
.container {
padding: 10px;
}
ul li {
list-style: none;
}
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn./bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<div class="container">
<div class="row">
<div class="col-sm-12 mb-3">
<input type="text" id="myFilter" class="form-control" onkeyup="myFunction()" placeholder="Search for names..">
</div>
</div>
<div class="row" id="myItems">
<div class="col-sm-12 mb-3">
<div class="card">
<div class="card-body">
<h5 class="card-title"><a href="#">Card One</a></h5>
<h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
<p class="card-text">Some text.</p>
</div>
</div>
<div class="card">
<div class="card-body">
<h5 class="card-title"><a href="#">Card Two</a></h5>
<h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
<p class="card-text">Some text.</p>
</div>
</div>
<div class="card">
<div class="card-body">
<h5 class="card-title"><a href="#">Card Three</a></h5>
<h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
<p class="card-text">Some text.</p>
</div>
</div>
</div>
</div>
</div>
Implementation B
A more robust solution would be to iterate over the cards instead of over the titles. That way you directly have a reference to the card and don't have to fiddle with parentElements. When you only want to search on text it might also be useful to use the innerText property to acces the string of text inside your card titles.
function myFunction() {
var input, filter, cards, cardContainer, h5, title, i;
input = document.getElementById("myFilter");
filter = input.value.toUpperCase();
cardContainer = document.getElementById("myItems");
cards = cardContainer.getElementsByClassName("card");
for (i = 0; i < cards.length; i++) {
title = cards[i].querySelector(".card-body h5.card-title a");
if (title.innerText.toUpperCase().indexOf(filter) > -1) {
cards[i].style.display = "";
} else {
cards[i].style.display = "none";
}
}
}
.container {
padding: 10px;
}
ul li {
list-style: none;
}
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn./bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<div class="container">
<div class="row">
<div class="col-sm-12 mb-3">
<input type="text" id="myFilter" class="form-control" onkeyup="myFunction()" placeholder="Search for names..">
</div>
</div>
<div class="row" id="myItems">
<div class="col-sm-12 mb-3">
<div class="card">
<div class="card-body">
<h5 class="card-title"><a href="#">Card One</a></h5>
<h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
<p class="card-text">Some text.</p>
</div>
</div>
<div class="card">
<div class="card-body">
<h5 class="card-title"><a href="#">Card Two</a></h5>
<h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
<p class="card-text">Some text.</p>
</div>
</div>
<div class="card">
<div class="card-body">
<h5 class="card-title"><a href="#">Card Three</a></h5>
<h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
<p class="card-text">Some text.</p>
</div>
</div>
</div>
</div>
</div>
You must hide the whole card container instead of hiding the heading (h5) only.
A quick correction would be using parentNode
on the heading, for example
if (a.innerHTML.toUpperCase().indexOf(filter) > -1) {
h5[i].parentNode.style.display = "";
} else {
h5[i].parentNode.style.display = "none";
}
The problem here is that you're only setting display:none
and vice versa to the h5
and not the whole card itself.
See here
function myFunction() {
var input, filter, card, h5, a, i;
input = document.getElementById("myFilter");
filter = input.value.toUpperCase();
card = document.getElementById("myItems");
h5 = card.getElementsByTagName("h5");
for (i = 0; i < h5.length; i++) {
a = h5[i].getElementsByTagName("a")[0];
if (a.innerHTML.toUpperCase().indexOf(filter) > -1) {
h5[i].closest(".card").style.display = "";
} else {
h5[i].closest(".card").style.display = "none";
}
}
}
I have changed it so that it gets its closest parent which has a class of .card
here
h5[i].closest(".card")
Alternatively, if your target browser does not support .closest
you can use
h5[i].parentNode.parentNode.style.display = "none"
See the full Codepen I have forked here: https://codepen.io/anon/pen/gjKgjN
You are hiding the titles. What you want to hide is the card.
You can do this by doing h5[i].parentNode.parentNode since the title is nested 2 layers deep in the card.
If your clients browsers support it you can use Closest ancestor matching selector using native DOM?
or if you have jQuery at your disposal you can search for $(h5[i]).closest('.card');
function myFunction() {
var input, filter, card, h5, a, i;
input = document.getElementById("myFilter");
filter = input.value.toUpperCase();
card = document.getElementById("myItems");
h5 = card.getElementsByTagName("h5");
for (i = 0; i < h5.length; i++) {
var current = h5[i];
a = current.getElementsByTagName("a")[0];
if (a.innerHTML.toUpperCase().indexOf(filter) > -1) {
current.parentNode.parentNode.style.display = "";
} else {
current.parentNode.parentNode.style.display = "none";
}
}
}
.container {
padding: 10px;
}
ul li {
list-style: none;
}
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn./bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<div class="container">
<div class="row">
<div class="col-sm-12 mb-3">
<input type="text" id="myFilter" class="form-control" onkeyup="myFunction()" placeholder="Search for names..">
</div>
</div>
<div class="row" id="myItems">
<div class="col-sm-12 mb-3">
<div class="card">
<div class="card-body">
<h5 class="card-title"><a href="#">Card One</a></h5>
<h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
<p class="card-text">Some text.</p>
</div>
</div>
<div class="card">
<div class="card-body">
<h5 class="card-title"><a href="#">Card Two</a></h5>
<h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
<p class="card-text">Some text.</p>
</div>
</div>
<div class="card">
<div class="card-body">
<h5 class="card-title"><a href="#">Card Three</a></h5>
<h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
<p class="card-text">Some text.</p>
</div>
</div>
</div>
</div>
</div>
But I would actually remend you by using getElementsByClassName
to get all the cards, then loop through them, select the h5 element by the class name and then accessing the innerText
of the h5, that way non visible text like a link title="some thing cool here"
won't get in the way.
See the below snippet.
function myFunction() {
var input, filter, myItems, cards, i, current, h5, text;
input = document.getElementById("myFilter");
filter = input.value.toUpperCase();
myItems = document.getElementById("myItems");
cards = myItems.getElementsByClassName("card");
for (i = 0; i < cards.length; i++) {
current = cards[i];
h5 = current.getElementsByClassName('card-title')[0];
text = h5.innerText.toUpperCase();
if (text.indexOf(filter) > -1) {
current.style.display = "";
} else {
current.style.display = "none";
}
}
}
.container {
padding: 10px;
}
ul li {
list-style: none;
}
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn./bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<div class="container">
<div class="row">
<div class="col-sm-12 mb-3">
<input type="text" id="myFilter" class="form-control" onkeyup="myFunction()" placeholder="Search for names..">
</div>
</div>
<div class="row" id="myItems">
<div class="col-sm-12 mb-3">
<div class="card">
<div class="card-body">
<h5 class="card-title"><a href="#">Card One</a></h5>
<h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
<p class="card-text">Some text.</p>
</div>
</div>
<div class="card">
<div class="card-body">
<h5 class="card-title"><a href="#">Card Two</a></h5>
<h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
<p class="card-text">Some text.</p>
</div>
</div>
<div class="card">
<div class="card-body">
<h5 class="card-title"><a href="#">Card Three</a></h5>
<h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
<p class="card-text">Some text.</p>
</div>
</div>
</div>
</div>
</div>