Jquery click event triggers twice when clicked on html label

随声附和 提交于 2019-11-27 06:54:11

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');
    }
});

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

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');
    }
});

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

I just ran into this same issue where clicking on the label was triggering two separate click events. I was able to solve the problem for myself by adding a name attribute to the input and a for attribute to the label.

For example:

<div id="test">
    <label for="checkbox1">
        <input type="checkbox" name="checkbox1" id="1" />demo1</label>
    <label for="checkbox2">
        <input type="checkbox" name="checkbox2" id="2" />demo2</label>
    <label for="checkbox3">
        <input type="checkbox" name="checkbox3" id="3" />demo3</label>
 </div>

No JavaScript needed.

This happens mostly when you trigger 'click' for X element having child element Y and when user clicks on Y.

The event bubbled from actually clicked element is undesired event that you don't want.

You have two options to avoid this double triggering...

1) Use $('selector').triggerHandler('any_standard_event_name') - Here by standard event I mean 'click', 'change', 'hover' etc. (I have never tried this option)

2) Use custom event name. eg. $('selector').trigger('my.click');

Read more on http://api.jquery.com/trigger/

Regarding my case and the fact that I'm using material design's md-button the suggestions above didn't work very well because it dramatically reduced the footprint of the area receiving finger press on my android. Not to mention instead of just 2 tagNames I would get upwards of 3 sometimes (BUTTON, SPAN, or MD-ICON). Also the suggestion elsewhere on the web to change my version of Aria didn't help either. I ended up finding the problem was that I was loading jquery before angular.js even though ng-infinate-scroll's help doc says to load jquery first. Anyway, if I tried to load jquery after angular ng-infinate-scroll would quit working. So I downloaded the latest version of ng-infinate-scroll (non-stable) version 1.2.0 and now I can load my jquery after angular.js and my infinite scroll works all the issues I had with ng-click firing multiple times is gone.

My elements wrapped one another, also.

So i used a simple CSS trick for this solution :

.off{
   pointer-events:none;
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!