skip to Main Content

I have this scheme for multiselection of checkboxes, and I need p.label to trigger the .item checkbox (child of it);

But my JS script is working only when .labels are clicked, it selects/unselects the checkboxes, but when I click directly on the checkboxes inputs, I need that the checkboxes have the normal behavior of a checkbox, and ignore the onclick event added to all labels.

In short: I need the items in the list to be selectable through the label or directly in the box. How can I a fix my script to prevent colisions that are happening?

let fruits_labels = document.querySelectorAll("#fruits .label");
let fruits_items = document.querySelectorAll("#fruits .label .item"); 

fruits_labels.forEach((label, index) => {

  label.onclick = (event) => {

    console.log("index: " + index);

    fruits_items[index].checked = (fruits_items[index].checked == true) ? false : true;

  }

});

let colors_labels = document.querySelectorAll("#colors .label");
let colors_items = document.querySelectorAll("#colors .label .item");

colors_labels.forEach((label, index) => {

  label.onclick = (event) => {

    console.log("index: " + index);

    colors_items[index].checked = (colors_items[index].checked == true) ? false : true;

  }

});
div.container{ margin: 0; padding: 0; margin-bottom: 10px; width: 200px; border: 1px solid black; }

div.container p.label { margin: 0; padding: 4px 8px; display: flex; align-items: center; border: 1px solid magenta; }

div.container p.label:hover { background-color: yellow; }

div.container p.label input[type=checkbox].item{ margin-right: 6px; }
<div class="container" id="fruits">
  <p class="label"><input class="item" type="checkbox">APPLE</p>
  <p class="label"><input class="item" type="checkbox">BANANA</p>
  <p class="label"><input class="item" type="checkbox">KIWI</p>
  <p class="label"><input class="item" type="checkbox">LEMON</p>
  <p class="label"><input class="item" type="checkbox">ORANGE</p>
</div>

<div class="container" id="colors">
  <p class="label"><input class="item" type="checkbox">RED</p>
  <p class="label"><input class="item" type="checkbox">GREEN</p>
  <p class="label"><input class="item" type="checkbox">BLUE</p>
</div>

2

Answers


  1. Don’t re-invent the wheel. Your desired behaviour can be achieved natively with a <label> element:

    label { margin: 0; padding: 4px 8px; display: flex; align-items: center; border: 1px solid magenta; width: 200px; }
    label:hover { background-color: yellow; }
    <div class="container" id="fruits">
      <label for='APPLE' class="label"><input id='APPLE' class="item" type="checkbox" />APPLE</label>
      <label for='BANANA' class="label"><input id='BANANA' class="item" type="checkbox" />BANANA</label>
      <label for='KIWI' class="label"><input id='KIWI' class="item" type="checkbox" />KIWI</label>
      <label for='LEMON' class="label"><input id='LEMON' class="item" type="checkbox" />LEMON</label>
      <label for='ORANGE' class="label"><input id='ORANGE' class="item" type="checkbox" />ORANGE</label>
    </div>

    If you, for some reason can’t use the above, another option would be to check the firstChild of the click’s event.target. If that’s a checkbox, you’ll need to toggle it:

    [ ...document.querySelectorAll('.label') ].forEach(l => {
        l.addEventListener('click', (e) => {
            if (e.target.firstChild?.type === 'checkbox') {
                e.target.firstChild.checked = !e.target.firstChild.checked;
            }
        });
    });
    div.container{ margin: 0; padding: 0; margin-bottom: 10px; width: 200px; border: 1px solid black; }
    div.container p.label { margin: 0; padding: 4px 8px; display: flex; align-items: center; border: 1px solid magenta; }
    div.container p.label:hover { background-color: yellow; }
    div.container p.label input[type=checkbox].item{ margin-right: 6px; }
    <div class="container" id="fruits">
      <p class="label"><input class="item" type="checkbox">APPLE</p>
      <p class="label"><input class="item" type="checkbox">BANANA</p>
      <p class="label"><input class="item" type="checkbox">KIWI</p>
      <p class="label"><input class="item" type="checkbox">LEMON</p>
      <p class="label"><input class="item" type="checkbox">ORANGE</p>
    </div>
    Login or Signup to reply.
  2. While you could use labels and they would work nicely, as Ostone0 described, I will show you how your script can be fixed, so you will learn about propagation and stopPropagation, but you should not overcomplicate yourself and you should aim to simplify your code once your tests work.

    let fruits_labels = document.querySelectorAll("#fruits .label");
    let fruits_items = document.querySelectorAll("#fruits .label .item"); 
    
    fruits_labels.forEach((label, index) => {
    
      label.onclick = (event) => {
    
        console.log("index: " + index);
    
        fruits_items[index].checked = (fruits_items[index].checked == true) ? false : true;
    
      }
      
      label.querySelector('input[type=checkbox]').addEventListener("click", function(event) {
          console.log("index: " + index);
          event.stopPropagation();
      });
    
    });
    
    let colors_labels = document.querySelectorAll("#colors .label");
    let colors_items = document.querySelectorAll("#colors .label .item");
    
    colors_labels.forEach((label, index) => {
    
      label.onclick = (event) => {
    
        console.log("index: " + index);
    
        colors_items[index].checked = (colors_items[index].checked == true) ? false : true;
    
      }
    
      label.querySelector('input[type=checkbox]').addEventListener("click", function(event) {
          console.log("index: " + index);
          event.stopPropagation();
      });
    });
    div.container{ margin: 0; padding: 0; margin-bottom: 10px; width: 200px; border: 1px solid black; }
    
    div.container p.label { margin: 0; padding: 4px 8px; display: flex; align-items: center; border: 1px solid magenta; }
    
    div.container p.label:hover { background-color: yellow; }
    
    div.container p.label input[type=checkbox].item{ margin-right: 6px; }
    <div class="container" id="fruits">
      <p class="label"><input class="item" type="checkbox">APPLE</p>
      <p class="label"><input class="item" type="checkbox">BANANA</p>
      <p class="label"><input class="item" type="checkbox">KIWI</p>
      <p class="label"><input class="item" type="checkbox">LEMON</p>
      <p class="label"><input class="item" type="checkbox">ORANGE</p>
    </div>
    
    <div class="container" id="colors">
      <p class="label"><input class="item" type="checkbox">RED</p>
      <p class="label"><input class="item" type="checkbox">GREEN</p>
      <p class="label"><input class="item" type="checkbox">BLUE</p>
    </div>

    Events are bubbling, that is, the event of a tag propagates an event to its parent which propagates one to its parent and so on. So, when you click on the checkbox, then its default click handler changes the checked value and propagates to the parent, which has an event that negates the checked value. So, you can use stopPropagation to avoid the bubbling from happening.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search