4

Why when I click inside the box with the black border, the toggle doesn't execute but when I click outside, it does, but not the checkbox?

var checks = document.querySelectorAll("ul li");

for (var i = 0; i < checks.length; i++) {
  checks[i].addEventListener("click", tog);
};

function tog(e) {
  e.currentTarget.classList.toggle("active");
}
ul li {
  background: #3CF;
  padding: 0.25em 0.5em;
  margin: 0.25em 0;
  display: block;
  cursor: pointer;
  text-indent: 1.5em;
}
ul li.active {
  background: #6EF;
}
label {
  display: block;
  width: 100px;
  border: 1px solid black;
}
<ul>
  <li>
    <label>
      <input type="checkbox">1
    </label>
  </li>
  <li>
    <label>
      <input type="checkbox">2
    </label>
  </li>
  <li>
    <label>
      <input type="checkbox">3
    </label>
  </li>
</ul>
Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Vandervals
  • 5,774
  • 6
  • 48
  • 94

1 Answers1

2

No. As I explained in my other answer. Due to bubbling in and out of lable tag, the tog function is called twice. Hence li tag is receiving two same events one after the other, hence active class is added and removed back-to-back, causing this issue.

You can check this in fiddle with alert.


We can have workaround for this to avoid this situation, by using event's target property. Here is the fiddle which works as you asked.

var checks = document.querySelectorAll("ul li");

for (var i = 0; i < checks.length; i++) {
  checks[i].addEventListener("click", tog);
};

var i = 0;
function tog(e) {
        if(e.target.tagName.toLowerCase() == 'label') {
            i++;  //if we remove this then i will never increment
            if(i%2 != 0) {
                i++;  //to bring back to even, so next click should work fine
                return;
            }
        }
  e.currentTarget.classList.toggle("active");
}
ul li {
  background: #3CF;
  padding: 0.25em 0.5em;
  margin: 0.25em 0;
  display: block;
  cursor: pointer;
  text-indent: 1.5em;
}
ul li.active {
  background: #6EF;
}
label {
  display: block;
  width: 100px;
  border: 1px solid black;
}
<ul>
  <li>
    <label>
      <input type="checkbox">1
    </label>
  </li>
  <li>
    <label>
      <input type="checkbox">2
    </label>
  </li>
  <li>
    <label>
      <input type="checkbox">3
    </label>
  </li>
</ul>

You can avoid i global variable by using IIFE returning function handler or using closure.

Community
  • 1
  • 1
rajuGT
  • 6,224
  • 2
  • 26
  • 44
  • is it possible to avoid the second bubbling? – Vandervals Nov 11 '15 at 15:01
  • As I explained over the answer in comment. https://jsfiddle.net/hvv6ucu8/7/ Even, the event objects passed are same, so I updated my answer with possible workaround, but there is no straightforward way I guess. – rajuGT Nov 11 '15 at 15:29
  • can you extend the fiddle above? I can't see how that would work with more than one label and one `i` variable – Vandervals Nov 11 '15 at 15:41
  • Since the event is triggered back-to-back, and also JS is single threaded. If label triggers onclick, it MUST complete the other one, which will be fired next. So, I'm not holding state of `li` for each, because `i` will be in stable state always. (check comments in code). `i` is just used to check, is it the **second fire** or not. – rajuGT Nov 11 '15 at 15:46