I'm trying to make some kind of a filter, and I need some help with minimum change on my codes. I believe there must be a practical solution for what I want to do, and wanted to ask for your correction, please.
Button01 hides/shows all elements with the class name filter01,
Button02 hides/shows all elements with the class name filter02,
So, both of the buttons have control over ElementX.
What I want to do is, when a button is clicked once, ElementX must remain hidden until it's clicked again, even when the other button is also clicked after.
HTML:
<div onclick="filter01()">Filter 01</div>
<div onclick="filter02()">Filter 02</div>
<div >ElementX</div></a>
JS:
function filter01() {
var i;
var filter = document.getElementsByClassName("filter01");
for (i=0; i<filter.length; i ) {
if (filter[i].style.display) {filter[i].style.display = null; button01.style.color = null;}
else {filter[i].style.display = "none"; button01.style.color = "rgb(200,200,200)}
}
}
function filter02() {
var i;
var filter = document.getElementsByClassName("filter02");
for (i=0; i<filter.length; i ) {
if (filter[i].style.display) {filter[i].style.display = null; button02.style.color = null;}
else {filter[i].style.display = "none"; button.style.color = "rgb(200,200,200)}
}
}
CodePudding user response:
I would organise the event listening outside of the HTML. Instead define a data attribute on the buttons that determines what they hide. Handle the button clicks in a single function, which will then read the data attribute.
Define a Set that has all the selectors that select items that must be hidden. And toggle set elements in the click handler.
When adding a selector to hide, then just select those elements and hide them. If however, the selector is removed from the set, then first naively show those elements again, but then use the set to hide all elements that are selected by one of the selectors in the set.
Snippet:
for (let button of document.querySelectorAll(".filterbutton")) {
button.addEventListener("click", filter);
}
let filters = new Set;
function toggleDisplay(selector, display) {
let elems = document.querySelectorAll(selector);
for (let elem of elems) {
elem.style.display = display;
}
}
function filter() {
let filterSelector = this.dataset.filter;
let show = filters.delete(filterSelector);
this.style.color = show ? "" : "rgb(200,200,200)";
if (!show) {
filters.add(filterSelector); // toggle this filter
} else {
toggleDisplay(filterSelector, "");
}
if (filters.size) {
toggleDisplay([...filters].join(","), "none");
}
}
.filterbutton {
border: 1px solid;
display: inline-block;
background: lightblue;
padding: 5px;
cursor: pointer }
<div data-filter=".filter01">Filter 01</div>
<div data-filter=".filter02">Filter 02</div>
<div data-filter=".filter03">Filter 03</div>
<div >This element has filter01, filter02 and filter03</div>
<div >This element has filter01 and filter02</div>
<div >This element has filter01 and filter03</div>
<div >This element has filter02 and filter03</div>
<div >This element has filter01 only</div>
<div >This element has filter02 only</div>
<div >This element has filter03 only</div>
CodePudding user response:
Is this the solution you wanted? I have used window.getComputedStyle()
method to get the pointerEvents of the other button so I can toggle it according to its value. Same for other button as well. Check it out once.
function filter01() {
var i;
var filter = document.getElementsByClassName("filter01");
for (i=0; i<filter.length; i ) {
if (filter[i].style.display) {
filter[i].style.display = null;
filter[i].style.color = null;
} else {
filter[i].style.display = "none";
filter[i].style.color = "rgb(200,200,200)";
}
}
let buttons = document.getElementsByClassName("button02");
let styles = window.getComputedStyle(buttons[0]);
if(styles.pointerEvents === 'auto') {
buttons[0].style.pointerEvents = 'none';
} else if(styles.pointerEvents === 'none') {
buttons[0].style.pointerEvents = 'auto';
}
}
function filter02() {
var i;
var filter = document.getElementsByClassName("filter02");
for (i=0; i<filter.length; i ) {
if (filter[i].style.display) {
filter[i].style.display = null;
filter[i].style.color = null;
}
else {
filter[i].style.display = "none";
filter[i].style.color = "rgb(200,200,200)"
}
}
let buttons = document.getElementsByClassName("button01");
let styles = window.getComputedStyle(buttons[0]);
if(styles.pointerEvents === 'auto') {
buttons[0].style.pointerEvents = 'none';
} else if(styles.pointerEvents === 'none') {
buttons[0].style.pointerEvents = 'auto';
}
}
<div onclick="filter01()">Filter 01</div>
<div onclick="filter02()">Filter 02</div>
<div >ElementX</div></a>
CodePudding user response:
First... Using onclick
attributes is so 2002... Use .addEventListener.
Then, using some data- attributes and dataset is the key here, as more than on filter could toggle the hidden state of an element.
Finally, to hide an element, use a class.
const filterBtns = document.querySelectorAll(".filterBtn");
const elements = document.querySelectorAll(".element");
fiterElements = (filterClass) => {
elements.forEach((element) => {
const hiddenByThisClass =
element.dataset.hiddenby.indexOf(filterClass) > -1;
const alreadyHidden = element.classList.contains("filterActive");
// for elements having the filterClass
// if already hidden by this filterClass ( may be hidden by another)
// Remove the class from the hiddenby dataset
if (element.classList.contains(filterClass) && hiddenByThisClass) {
element.dataset.hiddenby = element.dataset.hiddenby.replace(
" " filterClass,
""
);
}
// Same as above, but adds the class
if (element.classList.contains(filterClass) && !hiddenByThisClass) {
element.dataset.hiddenby = " " filterClass;
}
// Finally, decide to add or remove the filterActive class
// Show only when hiddeby is an empty string
element.dataset.hiddenby === ""
? element.classList.remove("hidden")
: element.classList.add("hidden");
});
};
filterBtns.forEach((btn) =>
btn.addEventListener("click", (e) => {
// Get the clicked filter button
const target = e.target;
// Make it appear active
target.classList.toggle("filterActive");
// Get the filter number
const elementClass = target.dataset.filternum;
// Pass it to the filter function
fiterElements("filter" elementClass);
})
);
.hidden {
display: none;
}
.filterBtn {
border: 1px solid black;
border-radius: 6px;
padding: 4px;
margin: 4px 0;
cursor: pointer;
width: 4em;
}
.filterActive {
background-color: blue;
color: white;
}
<div data-filternum="01">Filter 01</div>
<div data-filternum="02">Filter 02</div>
<div data-hiddenby="">Element 1 and 2</div></a>
<div data-hiddenby="">Element 1 only</div></a>
<div data-hiddenby="">Element 2 only</div></a>