11

I have this code:

<label><input type="checkbox">True?</label>

and

$("label").click(function () {
  $(this).toggleClass("active");
});

When I click the checkbox class toggles, when I click "True?" nothing happens. Why?

Then if I don't wrap checkbox in label everything works fine.

<input type="checkbox" id="c1"><label for="c1">True?</label>

This is so weird... And it doesn't matter if I put "for" or not.

http://jsfiddle.net/zufs6ueh/1/

What is going wrong here?

artemean
  • 805
  • 1
  • 10
  • 24
  • 3
    Does the `onclick` fire twice? `label` interacts with `input` in a specific way. – Halcyon Jan 19 '15 at 17:02
  • not sure why, I think the label's click event might be reassigned to the input when it's contained in the label or something like that, but a solution to it could be to bind the event to the input and change the parent's label like `$("label input").click(function () { $(this).parent('label').toggleClass("active"); });` – Dave Goten Jan 19 '15 at 17:08
  • @Halcyon Yes, it seems that onclick fires twice, because I see empty "class" in web inspector. Can this be fixed? – artemean Jan 19 '15 at 17:08

3 Answers3

11

This would be safer to use:

$(function() {

    $('input[type="checkbox"]').bind('change', function (v) {

        if($(this).is(':checked')) {
            $(this).parent().addClass('active');
        } else {
            $(this).parent().removeClass('active');
        }
    });

});

Using change instead of click allows for people that navigate forms using the keyboard.

rwacarter
  • 1,915
  • 1
  • 14
  • 25
  • 1
    Your answer is good by suggesting to bind event on the input, and not on all labels. Anyway, if you mean use spacebar to check the checkbox when you say *"allows for people that navigate forms using the keyboard"*, it think there is no problem at all since the spacebar triggers the `click` as well. +1 – DontVoteMeDown Jan 19 '15 at 17:15
  • @DontVoteMeDown thanks, I did not know the spacebar triggers `click`. You learn something every day! But yes you're right, the main issue here is attaching the event to the checkbox as opposed to the label. – rwacarter Jan 19 '15 at 17:18
  • Ok, this works fine, but I still need to know what is wrong with my original example. – artemean Jan 19 '15 at 17:19
  • @Andrey as has been mentioned before, when clicking the label the 'click' event is firing twice. You can add `evt.stopPropagation(); evt.preventDefault();` to the end of the `click` function to prevent this. (And `evt` as the argument of the function) – rwacarter Jan 19 '15 at 17:23
  • @rwacarter, yes but why is it firing twice? Isn't it weired? Maybe it's a bug? ;-) – artemean Jan 19 '15 at 17:37
  • @Andrey it's to do with the union between a label and an input within it. A similar question is answered here: http://stackoverflow.com/questions/17185265/jquery-click-event-triggers-twice-when-clicked-on-html-label – rwacarter Jan 19 '15 at 17:46
  • This is awesome :) thak – hkucuk Oct 01 '15 at 19:31
  • Is simple: Label process the event, then passes it down int children - the input. – horiatu Mar 27 '18 at 17:31
2

Clicking the label also triggers a click on the input so bind the change event to the checkbox itself:

$(function(){
    $("label input").on("click",function(){
 $(this).parent().toggleClass("active");
    });
});
.active {color:red;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<label><input type="checkbox" />True?</label>

Or With CSS 3

If you don't need to support old browsers, you could use the :checked pseudo-class in CSS instead:

input[type=checkbox]:checked + label {color:red;}
<input type="checkbox" id="demo" name="demo"> 
<label for="demo">True?</label> 
Moob
  • 14,420
  • 1
  • 34
  • 47
  • I'm always a fan of avoiding javascript when unnecessary, but just a heads up that the `:checked` pseudo class won't work in IE<9 – rwacarter Jan 19 '15 at 17:26
  • @rwacarter - Agreed. Annoying isn't it? Ideally you'd test for support then use a JS fallback. – Moob Jan 19 '15 at 17:37
  • @Moob, your code does the same thing @rwacarter suggested, but still it's not clear why `onclick` fires twice... As for JS I need it to do other things after the label is clicked, so can't avoid it. – artemean Jan 19 '15 at 17:43
1

You're really close @Andrey, some noteworthy things about HTML inputs;

  • Both radio and checkbox HTML types can have an onclick call out to JavaScript

  • Both radio and checkbox HTML types have the .checked property that maybe inspected by JavaScript and the :checked pseudo class within CSS

  • Using names on radio HTML types allows for grouping things

Here's some example code that does stuff with a checkbox...

/**
 * Example JavaScript function for HTML input radio or checkbox types to call
 * @returns {boolean}
 */
function checkCheckbox(checkbox) {
  if (checkbox.checked != true) return false;
  /* Do JavaSript things when checkbox is checked */
  console.log('checkbox.checked -> ' + checkbox.checked);
  window.alert('checkbox.value -> ' + checkbox.value);
  return true;
}
/**
 * Hide things that should not be seen by just anyone
 */
.menu__checkbox,
.menu__container {
  display: none;
  visibility: hidden;
  opacity: 0;
  filter: alpha(opacity=0); /* Old MS compatibility */
}

/**
 * Stylize the label some
 */
.menu__label {
  display: block;
  width: 20px;
  height: 100%;
  transform: rotate(90deg);
}


/**
 * Show some things when checkbox is checked
 * Note, if feeling adventurous try using a `~` instead of `+`
 */
.menu__checkbox:checked + .menu > .menu__container {
  display: block;
  visibility: visible;
  opacity: 1;
  filter: alpha(opacity=100);
}

/**
 * Oh and un-rotate label when checkbox is checked too
 */
.menu__checkbox:checked + .menu > .menu__label {
  transform: rotate(0deg);
}
<input type="checkbox"
       class="menu__checkbox"
       value="valuable_checkbox"
       onclick="checkCheckbox(this);"
       id="trigger-menu">


<div class="menu">

  <div class="menu__container">
    <p class="menu__description">
      Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
      tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
      proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
    </p>
  </div>

  <label for="trigger-menu"
         class="menu__label">
         &#x25B2;
  </label>

</div>

... if ya look real close, targeting via trigger-menu shared between the above label's for and the input's id is doing some things with CSS and HTML that many might resort to JavaScript to do... though placing the checkbox so far outside of the menu's scope was only for demonstration; just 'cause something can be done doesn't always mean should in other words.

Like I eluded to the question's code was really close...

<input type="checkbox" id="c1"><label for="c1">True?</label>

... probably would be functional with...

<input id="c1" type="checkbox" onclick="window.alert('this.checked -> ' + this.checked);">
<label for="c1">Is checked?</label>

And as I also hinted that radio typed inputs could be use similarly, the following is a preview of something that may be made public one day within something ridiculous...

    <div class="menu__style">
      <input type="radio"
             name="colorize"
             value="Default"
             onclick="color_picker.unsetAlternateStyleSheet()"
             class="menu__style__item"
             id="style-default">
      <label for="style-default"
             class="menu__style__label">Default Style</label>
      <br>

      <input type="radio"
             name="colorize"
             value="AlternateStyleOne"
             onclick="color_picker.setAlternateStyleSheet(title = this.value)"
             class="menu__style__item"
             id="style-one">
      <label for="style-one"
             class="menu__style__label">First Alternative Style</label>
      <br>

      <input type="radio"
             name="colorize"
             value="AlternateStyleTwo"
             onclick="color_picker.setAlternateStyleSheet(title = this.value)"
             class="menu__style__item"
             id="style-two">
      <label for="style-two"
             class="menu__style__label">Second Alternative Style</label>
    </div>
S0AndS0
  • 860
  • 1
  • 7
  • 20