Jquery click event triggers twice when clicked on html label

72,805

Solution 1

The one of $('#test checkbox') is never called, because you don't have a tag with name checkbox.

And depending if you click on checkbox or the label, the callback for $('#test label') is called once or twice cause of the bubbling because the input element is part of the label and is one union and therefore also received the event if the label is click (it is not bubbling, you can't do event.stopPropagation()).

You can check this if you change your code this way:

 $('#test label').live('click', function (event) {
        event.stopPropagation(); //<-- has no effect to the described behavior
        console.log(event.target.tagName);
 });

Click on label:

LABEL
INPUT

Click on input:

INPUT

EDIT If you only want to handle the click on the label and not the checkbox - and you can't change your HTML structure - you can just ignore the event of the input element.

Either this way:

$('#test label').live('click', function (event) {
    if( event.target.tagName === "LABEL" ) {
         alert('clicked');
    }
});

Or using jQuery to test:

$('#test label').live('click', function (event) {
    if( $(event.target).is("label") ) {
         alert('clicked');
    }
});

Solution 2

It's because you put the input inside the label so when you click the checkbox you also click every parents (that's called bubbling) EDIT, credit to @t.niese : in fact there is no bubbling here because the issue is when you click on label and it only bubbles up.

To prevent double click event but also check the checkbox you can use :

$(document).ready(function () {

    $('#test label').on('click', function (event) {
        event.preventDefault();
        var $check = $(':checkbox', this);
        $check.prop('checked', !$check.prop('checked'));
        alert('clicked label');
    });


    $('#test :checkbox').on('click', function (event) {
        event.stopPropagation();
        alert('clicked checkbox');
    });
});

FIDDLE

Also prefer the usage of $.on and note the usage of :checkbox selector or [type="checkbox"] which according to JQuery API is faster and more compatible (attribute selectors)

event.preventDefault() will stop every action handled by browser for the native tag event.stopPropagation() prevents bubbling and any parent handlers from being notified of the event

Solution 3

The OP found one solution to prevent the click event handler for the label from getting executed twice for a single click. The selected answer also has a good solution, but it incorrectly states that this has nothing to do with event bubbling.

The reason there are two clicks does have to do with event bubbling.

Clicking on a label that is associated with an input causes two click events to be triggered. The first click event is triggered for the label. The default handling of that click event causes a second click event to get triggered for the associated input. If the input is a descendent of the label, the second click event bubbles up to the label. That is why the click event handler for the label is called twice.


Therefore one way to solve this problem (as the OP discovered) is to not place the input inside the label element. There will still be two click events, but the second click event will not bubble up from the input to the label.

When the input is not inside the label, the label's "for" attribute can be used to associate the label with the input. The "for" attribute must be set to the "id" value of the input.

<input type="checkbox" id="1" />
<label for="1">demo1</label>

Another solution would be to stop the event from bubbling up from the input:

$('#test :checkbox').on('click', function(event) {
    event.stopPropagation();
});

$('#test label').on('click', function() {
    alert('clicked');
});

Then there's the solution presented in the selected answer, which is to have the label's click handler ignore the event when it bubbles up from the input. I prefer to write it like this though:

$('#test label').on('click', function(event) {
    if (event.target == this) {
        alert('clicked');
    }
});

Solution 4

You MUST use preventDefault in your label and stopPropagation in your checkbox:

http://jsfiddle.net/uUZyn/5/

The Code:

$(document).ready(function () {

    $('#test label').on('click', function (event) {
      event.preventDefault();        
      alert('clicked label');
      var ele = $(this).find('input');
    if(ele.is(':checked')){
      ele.prop('checked', false);        
    }else{
      ele.prop('checked', true);
    }
  });

  $('#test :checkbox').on('click', function (event) {
    event.stopPropagation();
    alert('clicked checkbox');
  });
});

In this way you avoid the behaviour @t.niese explains

Thank you to the previous answers, I wouldn't figure out without them, :D.

Update

As t.niese points up, here is the answer I've updated with the behaviour:

http://jsfiddle.net/uUZyn/6/

Just added the check behaviour after using the preventDefault XD

Share:
72,805
sravis
Author by

sravis

SaaS Architect, Micro-Services, Full Stack Javascript, Node.js, Vue.JS, Kubernetes, Docker Developer

Updated on March 30, 2020

Comments

  • sravis
    sravis about 4 years

    I have an div container which contains html checkbox and its label. Using jquery i wanted to trigger an click event when someone clicks on label in this container.

    I see that jquery click event triggers twice when i click on label!

    For testing purpose i triggered click event on checkbox instead of label and here checkbox click event triggers only once as it should be.

    Here is the fiddle. http://jsfiddle.net/AnNvJ/2/

    <tr>
        <td>
            <div id="test">
                <label>
                    <input type="checkbox" id="1" />demo1</label>
                <label>
                    <input type="checkbox" id="2" />demo2</label>
                <label>
                    <input type="checkbox" id="3" />demo3</label>
            </div>
        </td>
        <td>&nbsp;</td>
        <td>&nbsp;</td>
    </tr>
    

    JQUERY

    $(document).ready(function () {
    
        $('#test label').live('click', function (event) {
            alert('clicked');
        });
    
    
        $('#test checkbox').live('click', function (event) {
            alert('clicked');
        });
    
    });