0

I have a simple function that is supposed to check if an element has a class. If it doesnt have the class, it should add the class. It should also iterate through all other elements in the series, and deselect them. So only 1 element should be shown at any time. I have looked at this, this, this and this as well as a bunch of others. I know the function should be simple, but I just cant resolve it.

const changeFilter = (itemNumber) => {
// grab element that has been clicked
let selector = document.getElementsByClassName(itemNumber);
// check if element doesnt has the class name that underlines it
if (!selector[0].classList.contains("darken-filter-selector")) {
  // add the class if it doesnt have it
  selector[0].classList.add("darken-filter-selector")
} else {
  // iterate through all the elements, check if it has the class
  // remove it if it doesnt have the class
  for (let i = 0; i < 7 ; i++) {
    if(i !== itemNumber) {
      return selector[i].classList.contains("darken-filter-selector") ? selector[0].classList.remove("darken-filter-selector"):null
    }
  }
}
};

And it should not look like this (what is currently happening) gif of incident

but rather should only allow for one selected element at a time...

IWI
  • 1,528
  • 4
  • 27
  • 47

4 Answers4

0

The simplest way is to store the previous itemNumber into global variable like previousItemNumber.

  • Set the initial value of previousItemNumber to -1 (lower than 0) so that it can be used on changeFilter function.
  • And on changeFilter function, first, you check if previousItemNumber is existed or not (in other words, already selected item is existed or not when selecting new item) and if existed, remove the className there.
  • Add the className for the selected item and set the current itemNumber to previousItemNumber.

let previousItemNumber = -1;

const changeFilter = (itemNumber) => {
  // grab element that has been clicked
  let selector = document.getElementsByClassName(itemNumber);
  
  if (previousItemNumber === itemNumber) {
    return;
  }
  
  // Remove the class from previous selector
  if (previousItemNumber >= 0) {
    selector[previousItemNumber].classList.remove("darken-filter-selector");
  }
  selector[itemNumber].classList.add("darken-filter-selector");
  previousItemNumber = itemNumber;
};
Derek Wang
  • 10,098
  • 4
  • 18
  • 39
0

You can achieve the desired using input type="checkbox" and a bit of JS:

const EL_checkboxesOne = document.querySelectorAll(".checkboxesOne");

const checkboxesOne = (EL_group) => {
  const inputs = EL_group.querySelectorAll('[type="checkbox"]');
  EL_group.addEventListener("input", (ev) => {
    [...inputs].filter(EL => EL !== ev.target).forEach(EL => EL.checked = false);
  });
};

EL_checkboxesOne.forEach(checkboxesOne);
.checkboxesOne {
  display: flex;
  font: bold 16px/1.4 sans-serif;
  color: #666;
}

.checkboxesOne input {
  height: 1px;
  overflow: hidden;
  position: absolute;
  white-space: nowrap;
  width: 1px;
  clip: rect(0 0 0 0);
  clip-path: inset(100%);
}

.checkboxesOne span {
  padding: 10px 0;
  margin: 0 10px;
  user-select: none;
  cursor: pointer;
}

.checkboxesOne input:checked + span {
  box-shadow: 0 4px green;
}
<div class="checkboxesOne">
  <label><input type="checkbox" name="filter" value="all"><span>ALL</span></label>
  <label><input type="checkbox" name="filter" value="new"><span>NEW</span></label>
  <label><input type="checkbox" name="filter" value="wip"><span>WIP</span></label>
  <label><input type="checkbox" name="filter" value="hot"><span>HOT</span></label>
  <label><input type="checkbox" name="filter" value="won"><span>WON</span></label>
  <label><input type="checkbox" name="filter" value="lost"><span>LOST</span></label>
  <label><input type="checkbox" name="filter" value="dnc"><span>DNC</span></label>
</div>
Roko C. Buljan
  • 196,159
  • 39
  • 305
  • 313
  • I would much rather use the html that i was given instead of changing everything to a checkbox system. – IWI Oct 14 '20 at 14:49
0

I went with this function, as I thought it was the easiest and required the least modification from my original function

  const changeFilter = (itemNumber) => {
    // grab element that has been clicked
    let selector = document.getElementsByClassName(itemNumber);
    // check if element doesnt has the class name that underlines it
    if (!selector[0].classList.contains("darken-filter-selector")) {
      // add the class if it doesnt have it
      selector[0].classList.add("darken-filter-selector");
    }
    // iterate through all the elements, check if it has the class
    // remove it if it doesnt have the class
    for (let i = 0; i < 7; i++) {
      if (i !== itemNumber) {
        let otherEls = document.getElementsByClassName(i);
        if (otherEls[0].classList.contains("darken-filter-selector")) {
          otherEls[0].classList.remove("darken-filter-selector");
        }
      }
    }
  };
IWI
  • 1,528
  • 4
  • 27
  • 47
0

This is my suggestion. It uses Event Delegation. Change the style as you like.

options.onclick = function(e) {
  if (e.target.tagName != "LI") return;
  var selected = options.querySelector(".active");
  if (selected) {
    selected.removeAttribute("class");
  }
  e.target.setAttribute("class", "active");
  console.log(e.target.dataset.option);
};
#options {
  display: flex;
  justify-content: center;
}

#options li {
  margin: 0 10px 0 10px;
  padding: 10px;
  cursor: pointer;
  list-style: none;
}

#options li.active {
  background-color: green;
  pointer-events: none;
  border-radius: 5px;
}
<ul id="options">
  <li data-option="all">ALL</li>
  <li data-option="new">NEW</li>
  <li data-option="wip">WIP</li>
  <li data-option="hot">HOT</li>
  <li data-option="won">WON</li>
  <li data-option="lost">LOST</li>
  <li data-option="dnc">DNC</li>
</ul>

You can also do without using Data Attribute (data-option="..."): just use e.target.innerHTML.

AbsoluteBeginner
  • 2,160
  • 3
  • 11
  • 21