Why are there two click events registered in this html / css / jquery

I am trying to create a list of checkboxes. I added my styles and they are displayed correctly when rendering. I want to add a class when the label for this checkbox is clicked. This is my markup, and here is the same in jsfiddle . In my violin you can see that two click events are logged with one click. Why?

HTML:

<ul> <li> <label for="test_0" class=""> <input id="test_0" name="offering_cycle" type="checkbox" value="1"> Fall </label> </li> <li> <label for="test_1" class=""> <input id="test_1" name="offering_cycle" type="checkbox" value="2"> Spring </label> </li> <li> <label for="test_2" class=""> <input id="test_2" name="offering_cycle" type="checkbox" value="3"> Summer </label> </li> <li> <label for="test_3" class=""> <input id="test_3" name="offering_cycle" type="checkbox" value="4"> Other </label> </li> </ul> 

CSS

 ul { list-style-type:none; } label { position:relative; display:inline-block; padding-left:27px; height:25px; } label:before { display:block; position:absolute; top:-2px; margin-left:-28px; width:18px; height:18px; background-color:#fff; border-radius:5px; border:1px solid #ccc; text-align: center; color:#fff; font-size:18px; content:'a'; } input { width:1px; height:1px; border:0; opacity:0; float:right; } 

JQuery

 $('label[for*=test_]').on('click',function(){ $(this).toggleClass('testing'); }); 
+7
javascript jquery html css javascript-events
source share
9 answers

The reason the label tag handler is called twice:

Clicking on a label associated with input triggers two button press events. The first click event fires for the tag. The default processing of this click event triggers a second click event that will be triggered for the corresponding input. Since you have an input as a descendant of the label, the second click event bubbles up to the label. This is why your click event handler is called twice.


If you really want to handle the click event for a label (and execute it only once for a click):

(1) If you want and can change the HTML, you can move the input so that it is not a descendant of the label. There will still be two click events, but the second click event will not bubble from the entrance to the label, since the label is no longer the ancestor of the entrance.

When an input is not a descendant of a label, you must use the label attribute for the attribute associated with it. The value for the for attribute must be the id value for input. (You have already included the for attribute with the appropriate value.)

 <input id="test_0" name="offering_cycle" type="checkbox" value="1"> <label for="test_0" class="">Fall</label> 

(2) Preventing the default processing of the first click event prevents the second click event from triggering , but this violates the label . That is, the flag will not be checked / unchecked when the label is pressed.

 $('label[for*=test_]').on('click', function(event) { event.preventDefault(); $(this).toggleClass('testing'); // returning false would be another way to prevent the default handling. }); 

jsfiddle


(3) Instead, you can stop the second-click event from the bubbles from the input.

 $('input:checkbox').on('click', function(event) { event.stopPropagation(); }); $('label[for*=test_]').on('click', function() { $(this).toggleClass('testing'); }); 

jsfiddle

Note. If the input was not a child of the label, this would be optional.


(4) Or you can check the purpose of the event in the handler. This will be the label for the first click event and input for the second. The following handler executes the code inside the if statement only for the first click event.

 $('label[for*=test_]').on('click', function(event) { if (event.target == this) { $(this).toggleClass('testing'); } }); 

jsfiddle

Note. If the input was not a child of the label, the code above will still work, but the if-statement will be unnecessary because the click event raised for input will not bubble up to the label.


Instead, click on the link:

In your case, you do not need to register a click handler for the label element. Instead, you can register a click (or change) handler for input. Then you can use $(this).closest('label') to get the label element.

 $('input[name=offering_cycle]').on('click', function() { $(this).closest('label').toggleClass('testing'); }); 

jsfiddle

Note. If the input was not a child of the label, the handler above will still be called when you click on the label, but $(this).closest('label') will not receive the label. Instead, you will need to use something like $('label[for="' + this.id + '"]') .


Regarding the "for" attribute of the label elements:

Since you have inputs inside labels, there is no need to include for attributes on labels --- but this is not invalid.

You set the "for" attribute values ​​to the "id" attribute values ​​for the input elements. This is the correct way to use the "for" attribute to match the label with the input. If you must include the for attribute with an invalid value, the label will not be associated with the input, even if the input is a descendant of the label.

From the HTML5 specification for the attribute for the label element:

The for attribute can be specified to indicate the form control by which the label should be associated. If an attribute is specified, the attribute value must be the identifier of the element to be marked in the same document as the label element. If an attribute is specified and the document has an element whose identifier is equal to the value of the for attribute, and the first such element is a marked element, then this element is a label element with a label.

If the for attribute is not specified, but the label element has an inherited descendant element, then the first such descendant in the order tree is the label element marked with an icon.

+1
source share

You can also try:

 $('label[for^="test_"]').click(function(){ $('div#target').append($(this).clone()); return false; }); 
+2
source share

This is because you are attaching an event to a label that contains a checkbox , and you use for="chekbox_id" . So, the event is fired for label , as well as for checkbox .

 <ul> <li> <label for="test_0" class="">Fall</label> <input id="test_0" name="offering_cycle" type="checkbox" value="1" /> </li> <li> <label for="test_1" class="">Spring</label> <input id="test_1" name="offering_cycle" type="checkbox" value="2" /> </li> <li> <label for="test_2" class="">Summer</label> <input id="test_2" name="offering_cycle" type="checkbox" value="3" /> </li> <li> <label for="test_3" class="">Other</label> <input id="test_3" name="offering_cycle" type="checkbox" value="4" /> </li> </ul> <div id="target"></div> 

Take input from label .

Demo: http://jsfiddle.net/tusharj/fdm1pmj2/5/

+1
source share

The for attribute on this label must be associated with the input name, not with the identifier.

You can call the stopPropagation method on the event, and you can enter the event in the click handler.

Example:

 $('label[for*=test_]').on('click',function(e){ e.stopPropagation(); }); 

Html example:

 <div class="parent something"> <div class="child something"> </div> </div> 

Now, if you have a div.something click handler, it will not fire twice, but you can still use both.

Sources:

0
source share

Okay, so I said in a comment. It generates two click events, does it instead, and works like a charm: Fiddle

 $('input').on('click',function(){ console.log("clicked"); $('div#target').append($(this).parent().clone()); }); 

You might want to rename your inputs.

0
source share

Rather try this.

Add a generic class to all the shortcuts and fire events on it. Like this:

 <ul> <li> <label for="test_0" class="testClass"> <input id="test_0" name="offering_cycle" type="checkbox" value="1"> Fall </label> </li> <li> <label for="test_1" class="testClass"> <input id="test_1" name="offering_cycle" type="checkbox" value="2"> Spring </label> </li> <li> <label for="test_2" class="testClass"> <input id="test_2" name="offering_cycle" type="checkbox" value="3"> Summer </label> </li> <li> <label for="test_3" class="testClass"> <input id="test_3" name="offering_cycle" type="checkbox" value="4"> Other </label> </li> </ul> 

And than

 $(".testClass").on('click',function(){ $(this).toggleClass('testing'); }); 
0
source share

The click event is fired twice: once for a label and once for a flag. You only need to check the box.

 $('label[for*=test_] > input').on('click',function(){ $('div#target').append($(this).parent().clone()); }); 

Jsfiddle here

0
source share

To explain what is going on:

  • The input is clicked, but since LABEL is bound to this input using the for attribute and for its simple child, here is your first recorded event.
  • How the input extends per click (all elements spread clicks) to LABEL, and since the shortcut is your delegated element, your second event is triggered here.

Since you wrapped your input flags in the LABEL element,
you can actually listen even on input :

 $('label[for*=test_] input').on('click',function(){ $('div#target').append($(this).closest("label").clone()); }); 

and then clone the parent label. jsFiddle


Another way to prevent such a fraternity between LABEL and the internal INPUT is to stop the input to propagate an event that allows the bounding label -> set a task to it: fiddle

 $("label:input").on("click", function( evt ) { evt.stopPropagation(); }); $('label[for*=test_]').on('click',function(){ $('div#target').append($(this).clone()); }); 

Otherwise, you could prevent the default behavior of LABEL jsFiddle
which fires events from the associated INPUT and that using e.preventDefault() , but the flag (hidden) input will not be checked!

 $('label[for*=test_]').on('click',function( e ){ e.preventDefault(); $('div#target').append($(this).clone()); }); 

https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault

0
source share

you can try this ...

 $("label").find("*").click(function( event ) { event.stopPropagation(); }); $('label[for*=test_]').on('click',function(){ $(this).toggleClass('test'); }); 
0
source share

All Articles