Jasper
Jasper

Reputation: 2231

Javascript: select others

I am working on an API where the plan is that the users fill in a short checklist with multiple options before a POST request is made. For each section the users gets multiple options / buttons, but only 1 of them can be selected.

The selected button will get the class marked (Which changes the background color to green) whilst the others remain white (unless one of them is clicked, in which case it turns green and the others become white)

I have tried two different Javascript functions, but so far none of them were able to get this behavior to work.

Attempt 1:

function Label(self, group) {
  //    mark button
  let btns = document.getElementsByClassName(group);

  for (el in btns) {
      btns[el].classList.remove('marked') 
  }
  self.classList.add('marked');
}

Attempt 2 (a more explicit check to see if self is involved)

function Label(self, group) {
      //    mark button
      let btns = document.getElementsByClassName(group);

      for (el in btns) {
          if (btns[el] !== self) {
              btns[el].classList.remove('marked')
          }

      }
      self.classList.add('marked');
    }

My reasoning was to first remove the .marked class from all elements, and then set it on the this element. As the function is called with the onclick command in the HTML it knows which of the elements it is.

<div class="group1">
    <button onclick="Label(this, pt_toggle)" class="pt_toggle">Incorrect</button>
    <button onclick="Label(this, pt_toggle)" class="pt_toggle">Partially correct</button>
    <button onclick="Label(this, pt_toggle)" class="pt_toggle">Correct</button>
</div>

However, the functions did not behave as I hoped. It throws an Uncaught TypeError: Cannot read property 'remove' of undefined error and the class is not set.

Does anyone know what I am doing wrong?

Upvotes: 0

Views: 953

Answers (3)

Heretic Monkey
Heretic Monkey

Reputation: 12115

There is an element which performs this same kind of thing natively: the radio button. This makes it easier to code a more concise solution (well, except the CSS to make it look like more a button, but that's optional):

var buttons = Array.from(document.querySelectorAll(".group1 .button"));
buttons.forEach((el) => {
  el.addEventListener("click", function (e) {
    buttons.forEach((button) => button.classList.toggle("marked", button.querySelector("input").checked));
  });
});
/* Hide the radio button */
.button input { display: none; }
/* make it green when selected */
.button.marked { 
  background: green
}
/* make the label look more like a button */
.button { 
  font: caption;
  font-size: smaller;
  padding: 2px 6px;
  border: 1px solid #999;
  border-radius: 1px;
  background: white;
}
.button:hover {
  border-color: ThreeDDarkShadow;
}
<div class="group1">
    <label class="button"><input type="radio" name="pt_toggle">Incorrect</label>
    <label class="button"><input type="radio" name="pt_toggle">Partially correct</label>
    <label class="button"><input type="radio" name="pt_toggle">Correct</label>
</div>

Upvotes: 1

Jonathan Michalik
Jonathan Michalik

Reputation: 1532

You're running into a feature of the for..in loop syntax. When you perform:

for (el in btns) {
    if (btns[el] !== self) {
        btns[el].classList.remove('marked');
    }
}

every property in the btns object will be iterated over, including the length property which you won't be able to remove a class name from btns["length"].

If you switch to using a normal for loop using btns.length as the limit, you won't iterate over the non-button element properties in the object:

for (var el = 0; el < btns.length; el++) {
    if (btns[el] !== self) {
        btns[el].classList.remove('marked');
    }
}

Upvotes: 1

dotnetdev4president
dotnetdev4president

Reputation: 552

Adding "marked" class on click to your button:

Your html:

<button onclick="Label(event)" class="pt_toggle">Incorrect</button>

Your js:

function Label(event) {
  event.target.classList.add("marked");
}

Upvotes: 0

Related Questions