I have 3 buttons that represent a product category on my site. I want to load the page with the "All Products" button having an active class and then have that class removed and added to another button when clicked. There seems to be an issue that is causing an active class to be added twice to a button or not removed and so two buttons will be active at the same time.
Two active buttons at once and Active class added twice
I can fix the problem if I remove the active class from the "All Products" button but again I want the page to load with it set to active. These buttons also have to control the visibility of elements depending on what category they belong to.
Here is my code:
HTML:
<div class="list-group" id="myBtnContainer">
<button class="btn list-group-item active" onclick="filterSelection('all')">All Products</button>
<button class="btn list-group-item" onclick="filterSelection('shirts')">Shirts</button>
<button class="btn list-group-item" onclick="filterSelection('pants')">Pants</button>
</div>
HTML - Example Element:
<div class="col-lg-4 col-md-6 mb-4 filterDiv shirts">
<div class="card h-100 ">
<img class="card-img-top" src="blackshirt.png" alt="">
<div class="card-body">
<h4 class="card-title">
Black Shirt
</h4>
<h5>$24.99</h5>
<p class="card-text">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Amet numquam aspernatur!</p>
</div>
<div class="card-footer">
<i class="fa fa-search"></i> View Product
</div>
</div>
</div>
JavaScript
filterSelection("all")
function filterSelection(c) {
var x, i;
x = document.getElementsByClassName("filterDiv");
if (c == "all") c = "";
for (i = 0; i < x.length; i++) {
w3RemoveClass(x[i], "show");
if (x[i].className.indexOf(c) > -1) w3AddClass(x[i], "show");
}
}
function w3AddClass(element, name) {
var i, arr1, arr2;
arr1 = element.className.split(" ");
arr2 = name.split(" ");
for (i = 0; i < arr2.length; i++) {
if (arr1.indexOf(arr2[i]) == -1) {element.className += " " + arr2[i];}
}
}
function w3RemoveClass(element, name) {
var i, arr1, arr2;
arr1 = element.className.split(" ");
arr2 = name.split(" ");
for (i = 0; i < arr2.length; i++) {
while (arr1.indexOf(arr2[i]) > -1) {
arr1.splice(arr1.indexOf(arr2[i]), 1);
}
}
element.className = arr1.join(" ");
}
// Add active class to the current button (highlight it)
var btnContainer = document.getElementById("myBtnContainer");
var btns = btnContainer.getElementsByClassName("btn");
for (var i = 0; i < btns.length; i++) {
btns[i].addEventListener("click", function(){
var current = document.getElementsByClassName("active");
current[0].className = current[0].className.replace(" active", "");
this.className += " active";
});
}
Two active buttons at once
Check the logic within this function filterSelection.
Active class added twice
You can use toggle function from the classList attribute.
Show products according to the selected button
Use the data attributes and work with the classList attribute.
I.e: Set to your buttons: data-target='shirts', data-target='pants' and so on.
Use a class hide with this style: display: none
var btnContainer = document.getElementById("myBtnContainer");
var btns = btnContainer.getElementsByClassName("btn");
for (var i = 0; i < btns.length; i++) {
btns[i].addEventListener("click", function() {
var current = document.getElementsByClassName("active");
current[0].classList.toggle('active');
this.classList.toggle('active');
var target = this.dataset.target;
filterSelection(target);
});
}
function filterSelection(target) {
document.querySelectorAll('.filterDiv').forEach((div) => {
if (target === 'all' || div.classList.contains(target)) {
div.classList.remove('hide');
} else {
div.classList.add('hide');
}
});
}
.active {
background-color: lightgreen;
}
.hide {
display: none
}
<div class="list-group" id="myBtnContainer">
<button class="btn list-group-item active" data-target='all'>Show all</button>
<button class="btn list-group-item" data-target='shirts'>Shirts</button>
<button class="btn list-group-item" data-target='pants'>Pants</button>
</div>
<div class="col-lg-4 col-md-6 mb-4 filterDiv shirts">
<div class="card h-100 ">
<img class="card-img-top" src="blackshirt.png" alt="">
<div class="card-body">
<h4 class="card-title">
Black Shirt
</h4>
<h5>$24.99</h5>
<p class="card-text">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Amet numquam aspernatur!</p>
</div>
<div class="card-footer">
<i class="fa fa-search"></i> View Product
</div>
</div>
</div>
<div class="col-lg-4 col-md-6 mb-4 filterDiv pants">
<div class="card h-100 ">
<img class="card-img-top" src="blackshirt.png" alt="">
<div class="card-body">
<h4 class="card-title">
Pants
</h4>
<h5>$44.99</h5>
<p class="card-text">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Amet numquam aspernatur!</p>
</div>
<div class="card-footer">
<i class="fa fa-search"></i> View Product
</div>
</div>
</div>
Resource
Element.classList
Related
So I have a button that activates a function onClick, then I want to check all classes of my page with the id of this button. Then it should get the element Type and compare it to e.g. "icon". So if the element is an icon it should get a different style than a div.
I already have an approach but I am stuck:
HTML:
<div class="question-tile-container">
<button id="01" class="01 button" onClick="test(this)">
<p>How do I create an account?</p>
<i class="01 fa-regular fa-arrow-down-wide-short"></i>
</button>
<div class="01 answer">
Lorem ipsum dolor sit amet.
</div>
</div>
JS:
function test(ele) {
var id = ele.id
console.log(id)
elements = document.getElementsByClassName(id)
console.log(elements)
for(i=0;i<elements.length+1;i++){
if(elements.tagName == 'icon') {
// style icon
} else {
// style div
}
}
}
It should check all elements with the id, then compare the element type to a string. If it matches a specific string it should style in a different way, than it would if it matches to another string.
replace this elements.tagName == 'icon' with elements.nodeName == 'I'
function test(ele) {
var id = ele.id
console.log(id)
elements = document.getElementsByClassName(id)
console.log(elements)
for (i = 0; i < elements.length; i++) {
if (elements.nodeName == 'I') {
console.log(elements[i]);
// style icon
} else {
console.log(elements[i]);
// style div
}
}
}
<div class="question-tile-container">
<button id="01" class="01 button" onClick="test(this.id)">
<p>How do I create an account?</p>
<i class="01 fa-regular fa-arrow-down-wide-short"></i>
</button>
<div class="01 answer">
Lorem ipsum dolor sit amet.
</div>
</div>
I want an accordion to drop down when a user clicks on the greater than sign.
<div class="row">
<div class="col-2">
<p>DEP-08B827E791<button class="accordion"><i class="fa-solid fa-greater-than fa ps-1"></i></button></p>
</div>
<div class="col-3">
<P>Omodeko Divine</P>
</div>
<div class="col-2">
<p>EE</p>
</div>
<div class="col-3">
<p>[ETHIOPE EAST]</p>
<P>DELSU HEALTH CENTER ABRAKA</P>
</div>
<div class="col-2">
<button class="btn btn-primary">GENERATE ID</button>
</div>
<div class="panel" onclick="document.getElementByclassname(panel).style.display='none'">
<p>No, but there must be adequate evidence that would help to support your claim.</p>
</div>
</div>
This is the javascript
var acc = document.getElementsByClassName("accordion");
var i;
for (i = 0; i < acc.length; i++) {
acc[i].addEventListener("click", function() {
/* Toggle between adding and removing the "active" class,
to highlight the button that controls the panel */
this.classList.toggle("activee");
/* Toggle between hiding and showing the active panel */
var panel = this.nextElementSibling;
if (panel.style.display === "block") {
panel.style.display = "none";
} else {
panel.style.display = "block";
}});}
On click of the greater than sign, the .panel is supposed to dropdown.
Add onclick event to the greater than icon
Next add id to .panel div
Next in script write the logic for hide and show, please refer to below code snippet
function showDropDown(){
const targetDiv = document.getElementById("panelDivId");
if (targetDiv.style.display !== "none") {
targetDiv.style.display = "none";
} else {
targetDiv.style.display = "block";
}
}
<div class="row">
<div class="col-2">
<p>DEP-08B827E791<button class="accordion"><i class="fa-solid fa-greater-than fa ps-1" onclick="showDropDown()">greaterThanIcon</i></button></p>
</div>
<div class="col-3">
<P>Omodeko Divine</P>
</div>
<div class="col-2">
<p>EE</p>
</div>
<div class="col-3">
<p>[ETHIOPE EAST]</p>
<P>DELSU HEALTH CENTER ABRAKA</P>
</div>
<div class="col-2">
<button class="btn btn-primary">GENERATE ID</button>
</div>
<div class="panel" id="panelDivId" onclick="document.getElementByclassname(panel).style.display='none'">
<p>No, but there must be adequate evidence that would help to support your claim.</p>
</div>
</div>
I would like to know if it's possible to get the height of many element with offsetHeight
<div class="container">
<div class="card">
<h4 class="card__title">My Title</h4>
<div class="card__img">
<img src="image.jpg" alt="">
</div>
<div class="card__description">
<p>
Lorem ipsum dolor, sit amet consectetur adipisicing elit.
</p>
</div>
</div>
<div class="card">
<h4 class="card__title">Thailande</h4>
<div class="card__img">
<img src="image.jpg" alt="">
</div>
<div class="card__description">
<p>
Description 2
</p>
</div>
</div>
</div>
I would like to to have a translate on the .card__img of the height of .card__description on a 'mouseover', is it possible to get the offsetHeight using querySelectorAll('.card__description).offsetHeight and using a loop?
I tried but didin't work.
Thanks guys, I did it!
Here is my solution
const cards = document.querySelectorAll('.card')
function cardAnimation () {
for (let i = 0; i < cards.length; i++) {
let card = cards[i]
let cardImg = card.querySelector('.card__img')
let description = card.querySelector('.card__description')
let descriptionHeight = description.offsetHeight
card.addEventListener('mouseover', () => {
description.style.transform = `translateY(0)`;
cardImg.style.transform = `translateY(-${descriptionHeight}px) scale(1.2)`
})
card.addEventListener('mouseleave', () => {
description.style.transform = `translateY(100%)`;
cardImg.style.transform = `translateY(0px) scale(1)`
})
}
}
cardAnimation()
I'm trying to implement this normalize() function into my search function to make it accent insensitive, but I don't really understand how to do it. This is how I tried, but it messes up the whole search function. I would be really grateful if anyone could show me how to do this.
function myFunction() {
var input, filter, cards, cardContainer, title, i, filt, tit;
input = document.getElementById("myFilter");
filter = input.value.toUpperCase();
filt = filter.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
cardContainer = document.getElementById("myItems");
cards = cardContainer.getElementsByClassName("col-md-4");
for (i = 0; i < cards.length; i++) {
title = cards[i].querySelector(".card-body");
tit = title.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
if (tit.innerText.toUpperCase().indexOf(filt) > -1) {
cards[i].style.display = "";
} else {
cards[i].style.display = "none";
}
}
}
The working accent sensitive version:
function myFunction() {
var input, filter, cards, cardContainer, title, i;
input = document.getElementById("myFilter");
filter = input.value.toUpperCase();
cardContainer = document.getElementById("myItems");
cards = cardContainer.getElementsByClassName("col-md-4");
for (i = 0; i < cards.length; i++) {
title = cards[i].querySelector(".card-body");
if (title.innerText.toUpperCase().indexOf(filter) > -1) {
cards[i].style.display = "";
} else {
cards[i].style.display = "none";
}
}
}
and an example for the html I want to search in:
<div class="col-md-4">
<div class="card card-profile" style="min-height: 386px;" id="268">
<div class="card-avatar">
<a href="#">
<img class="img" src="assets/img/faces/avatar.png" />
</a>
</div>
<div class="card-body">
<div class="card-top">
<h4 class="card-category text-gray"><b>ááá</b><br>
<span style="color: black;">úú</span><br>
<span style="color: black;" ></span>
blabla</h4>
</div>
<hr>
<h5 class="card-description">
<i class="material-icons md-18">
mobile_friendly
</i>
<b>tel.:</b> 06301212121
</h5>
<div class="card-header" role="tab" id="heading268">
<h5 class="mb-0">
<div class="col text-center">
<a class="btn btn-dark" style="width: 100%;" data-toggle="collapse" data-parent="#accordion" href="#collapse268" aria-expanded="false" aria-controls="collapse268">Részletek<i class="material-icons">
keyboard_arrow_down
</i></a>
</div>
</h5>
</div>
<div id="collapse268" class="collapse" role="tabpanel" aria-labelledby="heading268">
<h5 class="card-description" style='display: none';>
<i class="material-icons">
mobile_friendly
</i>
<b>tel2.:</b>
</h5>
<h5 class="card-description">
<b> Leírás:</b>
leírás teszt </div>
</h5>
</div>
</div>
</div>
</div>
Here's the solution:
function myFunction() {
var input, filter, cards, cardContainer, title, i;
input = document.getElementById("myFilter");
filter = input.value.toUpperCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "");
cardContainer = document.getElementById("myItems");
cards = cardContainer.getElementsByClassName("col-md-4");
for (i = 0; i < cards.length; i++) {
title = cards[i].querySelector(".card-body");
if (title.innerText.toUpperCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").indexOf(filter) > -1) {
cards[i].style.display = "";
} else {
cards[i].style.display = "none";
}
}
}
I am attempting to create a page that is populated by many cards, using bootstrap 4's new card component.
I want to create a search bar, that when searched, filters out cards whose titles don't match the search query.
Here is a plunker of what I have in mind. Plunker
I would like the cards to get something like a display: none, or opacity:0 if they don't match.
I currently am attempting to write a function that onChange of the search bar does this. I'll post if I can get it figured out.
I've tried to use the built in snippet feature as well.
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>
<script src="https://cdn.rawgit.com/twbs/bootstrap/v4-dev/dist/js/bootstrap.js"></script>
<link href="https://cdn.rawgit.com/twbs/bootstrap/v4-dev/dist/css/bootstrap.css" rel="stylesheet" />
<div class="container">
<div class="row">
<div class="col-sm-4">
<input type="search" placeholder="Search......" name="search" class="searchbox-input" onkeyup="buttonUp();" required>
</div>
<div class="col-sm-4">
</div>
<div class="col-sm-4">
</div>
</div>
<div class="card-columns">
<div class="card">
<div class="card-block">
<h4 class="card-title">Card title that wraps to a new line</h4>
<p class="card-text">This is a longer card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
</div>
</div>
<div class="card card-block">
<blockquote class="card-blockquote">
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer posuere erat a ante.</p>
<footer>
<small class="text-muted">
Someone famous in <cite title="Source Title">Source Title</cite>
</small>
</footer>
</blockquote>
</div>
<div class="card">
<div class="card-block">
<h4 class="card-title">Card title</h4>
<p class="card-text">This card has supporting text below as a natural lead-in to additional content.</p>
<p class="card-text"><small class="text-muted">Last updated 3 mins ago</small>
</p>
</div>
</div>
<div class="card card-block card-inverse card-primary text-xs-center">
<blockquote class="card-blockquote">
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer posuere erat.</p>
<footer>
<small>
Someone famous in <cite title="Source Title">Source Title</cite>
</small>
</footer>
</blockquote>
</div>
<div class="card card-block text-xs-center">
<h4 class="card-title">Card title</h4>
<p class="card-text">This card has supporting text below as a natural lead-in to additional content.</p>
<p class="card-text"><small class="text-muted">Last updated 3 mins ago</small>
</p>
</div>
<div class="card">
</div>
<div class="card card-block text-xs-right">
<blockquote class="card-blockquote">
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer posuere erat a ante.</p>
<footer>
<small class="text-muted">
Someone famous in <cite title="Source Title">Source Title</cite>
</small>
</footer>
</blockquote>
</div>
<div class="card card-block">
<h4 class="card-title">Card title</h4>
<p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This card has even longer content than the first to show that equal height action.</p>
<p class="card-text"><small class="text-muted">Last updated 3 mins ago</small>
</p>
</div>
</div>
</div>
Here's a quick example of how you could do it using jQuery's contains selector:
$('.searchbox-input').change( function () {
$('.card').show();
var filter = $(this).val(); // get the value of the input, which we filter on
$('.container').find(".card-title:not(:contains(" + filter + "))").parent().css('display','none');
});
Currently this is set up to happen on change of the search input, you would probably want set up a submit button and have it fire on submit instead.
Bootply Example
Here are a few more modern options that are Bootstrap 4 or Bootstrap 5 friendly...
Bootstrap 5 (using JavaScript)
var buttonUp = () => {
const input = document.querySelector(".searchbox-input");
const cards = document.getElementsByClassName("card");
let filter = input.value
for (let i = 0; i < cards.length; i++) {
let title = cards[i].querySelector(".card-body");
if (title.innerText.indexOf(filter) > -1) {
cards[i].classList.remove("d-none")
} else {
cards[i].classList.add("d-none")
}
}
}
Demo
Bootstrap 4 (using jQuery)
// this overrides `contains` to make it case insenstive
jQuery.expr[':'].contains = function(a, i, m) {
return jQuery(a).text().toUpperCase()
.indexOf(m[3].toUpperCase()) >= 0;
};
var buttonUp = () => {
$('.card').removeClass('d-none');
var filter = $(this).val(); // get the value of the input, which we filter on
$('.card-deck').find('.card .card-body h4:not(:contains("'+filter+'"))').parent().parent().addClass('d-none');
}
Demo
Here is the simple solution.
$(document).ready(function(){
$('.searchbox-input').on("keyup", function() {
var value = $(this).val().toLowerCase();
$(".card").filter(function() {
$(this).toggle($(this).text().toLowerCase().indexOf(value) > -1)
});
});
});
$(document).ready(function() {
$('#searchForm').keyup(function(){
search_text($(this).val());
});
function search_text(value){
$('#search_section .card').each(function(){
var found = 'false';
$(this).each(function(){
if($(this).text().toLowerCase().indexOf(value.toLowerCase()) >= 0)
{
found = 'true';
}
});
if(found == 'true'){
$(this).show()
}
else {
$(this).hide();
}
})
}
});