132

I am using jQuery to create custom radio buttons and i have a problem. When clicking on the label that associated with the radio the click events fires twice, if i click only on the radio itself it's working fine (well actually it's not the radio i am clicking but the div that wraps the whole input and label). Here is the code:

The HTML:

 <div id="box">
     <asp:RadioButtonList ID="RadioButtonList1" runat="server">
         <asp:ListItem>RADIO1</asp:ListItem>
         <asp:ListItem>RADIO2</asp:ListItem>
         <asp:ListItem>RADIO3</asp:ListItem>
     </asp:RadioButtonList>
</div>

jQuery:

<script type="text/javascript">
       $(function () {
            $('#box').find('input:radio').each(function (i) {

            var input = $(this);
            // get the associated label using the input's id
            var label = $('label[for=' + input.attr('id') + ']');
            // wrap the input + label in a div
            $('<div class="custom-radio"></div>').insertBefore(input).append(label, input);

            var wrapperDiv = input.parent();

            // find all inputs in this set using the shared name attribute
            var allInputs = $('input[name=' + input.attr('name') + ']');

            // necessary for browsers that don't support the :hover pseudo class on labels
            label.hover(

            function () {
                $(this).addClass('hover');
            }, function () {
                $(this).removeClass('hover checkedHover');
            });

            //bind custom event, trigger it, bind click,focus,blur events
            wrapperDiv.bind('updateState', function () {
                if ($(this)[0].children[1].checked) {
                    allInputs.each(function () {
                        var curDiv = $('div > label[for=' + $(this).attr('id') + ']').parent();
                        curDiv.removeClass('custom-radio-checked');
                        curDiv.addClass('custom-radio');
                    });
                    $(this).toggleClass('custom-radio custom-radio-checked');
                }
                else {
                    $(this).removeClass('custom-radio-checked checkedHover checkedFocus');
                }

            })
            .trigger('updateState')
            .click(function () { console.log('click'); })
            .focus(function () {
                label.addClass('focus');
            }).blur(function () {
                label.removeClass('focus checkedFocus');
            });
        }); 
       });
   </script>

Is there any solution for this behaviour?

17 Answers 17

231

I tried adding the solution above by adding:

evt.stopPropagation();
evt.preventDefault();

but didn't work. However adding this:

evt.stopImmediatePropagation();

solved the problem! :)

Sign up to request clarification or add additional context in comments.

2 Comments

Perfect. This worked fine! Although evt.stopPropagation(); evt.preventDefault(); didn;'t
Thank you! Any idea why this works when the others don't?
143

Try adding:

evt.stopPropagation();
evt.preventDefault();

to the .bind() or .click(), whichever you're seeing. Also, add the parameter evt to the function, like function(evt) {...

4 Comments

why does this happen?
Because there are nested items. Each item in the hierarchy will bubble up the event.
If you are using this for a checkbox, this was also needed for me to enable the input to actually be checked: jQuery('input').prop('checked', true);
return false; is equivalent to `evt.stopPropagation(); evt.preventDefault(); (in jQuery)
83

Bind the click event to the input rather than the label. When the label is clicked - the event will still occur because, as Dustin mentioned, a click on the label triggers a click on the input. This will allow the label to hold its normal functionality.

$('input').click();

Instead of

$('label').click();

4 Comments

This solution also works if your markup uses the label-wrapping-the-input technique, and just saved my sanity :o)
you should actually bind to change even on the radio button since the text of a label is clickable -- they don't always click the radio button itself.
If using a Bootstrapesque label > input setup, this is THE answer, not an answer. Adding evt.stopPropagation() or evt.preventDefault() to your code, while effective, is a hack that should be avoided when the proper solution is much cleaner and more efficient.
Best answer. Propagated event might be very useful elsewhere
11

If you're trying to use an outer container as a click element you can also let the events bubble naturally and test for the expected element in your click handler. This scenario is useful if you're trying to style a unique click zone for a form.

<form>
<div id="outer">
    <label for="mycheckbox">My Checkbox</label>
    <input type="checkbox" name="mycheckbox" id="mycheckbox" value="on"/>
</div>
</form>
<script>
$('#outer').on('click', function(e){
    // this fires for #outer, label, and input
    if (e.target.tagName == 'INPUT'){
        // only interested in input
        console.log(this);
    }
});
</script>

2 Comments

This worked for me as i still wanted the radio button to be selected. I just didn't want the event handler do to everything twice.
This won't allow children to be selected. BTW, a better option to achieve the same functionality would be if (e.target == e.currentTarget) {}
10

To fix this the easy way, remove the "for" attribute on the label. A click on the label will also trigger a click on the associated element. (which in your case is firing your click event twice.)

Good luck

1 Comment

Indeed the quick solution. One might argue that it is messing with the markup, but I'd counter that the purpose of the "for" attribute is event propogation. So if you are using another mechanism (jquery) then by all means get rid of "for".
7

I usually use this synthax

.off('click').on('click', function () { console.log('click'); })

instead of

.click(function () { console.log('click'); })

Comments

7

I have tried by adding solution.

evt.stopPropagation();
evt.preventDefault();

but didn't work.

By adding

evt.stopImmediatePropagation();

solved the problem! :)

Comments

6

Best answer is hidden inside comments:

you should actually bind to change even on the radio button since the text of a label is clickable -- they don't always click the radio button itself. – chovy Dec 12 '13 at 1:45

This fiddle illustrates that all the other solutions – stopPropagation, stopImmediatePropagation, preventDefault, return false – either change nothing or destroy the checkbox/radio functionality). It also illustrates that this is a vanilla JavaScript problem, not a jQuery problem.

EDIT: Another working solution that I just found in another thread is to bind the onclick to the input rather than the label. Updated fiddle.

Comments

1

The problem with e.preventDefault(); is it stops the label click from checking the radio button.

A better solution would be to simply add a "is checked" quick check like so:

$("label").click(function(e){
  var rbtn = $(this).find("input");
  if(rbtn.is(':checked')){
  **All the code you want to have happen on click**
  }
)};

3 Comments

An even more succinct solution would be just using .mouseup rather than .click
This solution does not work if your code is also going to allow a user to deselect a radio button. The double fire causes serious troubles in this case.
@yoshyosh .mouseup would only work for mouse action. Remember checkboxes can also be checked with "space bar" key on the keyboard.
1

My problem is a bit different, as the evt.stopPropagation();evt.preventDefault(); doesn't work for me, I just add return false; in the end, then it works.

$("#addressDiv").on("click", ".goEditAddress", function(event) {
    alert("halo");
    return false;
});

1 Comment

Checkbox is not clicked after label click in this case
1

In my case the problem was that i had the click event in a function and the function was executed twice.... every execution of the function creates a new click event. -facepalm-

after moving the click event outside the function, everything worked as expected! :)

Comments

0

Try put your input tag outside trigger element, because label tag emulates click, so you will have always more than one call.

Comments

0

I had the same issue because I had nested my radio inside the label like this with the handler attached to radio_div. Removing the nested label fixed the issue.

<div id="radio_div">
    <label>
       <input type="radio" class="community_radio" name="community_radio" value="existing">
           Add To Existing Community
    </label>
</div>

Comments

0

The label triggers the radio/checkbox to be checked.

if ($(event.target).is('label')){
    event.preventDefault();
}

It prevent especially the label to trigger this behavior.

Comments

0

The click on the label with a for="some-id" attribute triggers a new click, but only if the target exists and is an input. I was not able to solve it perfectly with e.preventDefault() or stuff like that so I did it like this:

For example, if you have this structure and want an event on clicking .some-class

<div class="some-class">
    <input type=checkbox" id="the-input-id" />
    <label for="the-input-id">Label</label>
</div>

What did work was:

$(document)
    .on('click', '.some-class', function(e) {
        if(
            $(e.target).is('label[for]')
            &&
            $('input#' + $(e.target).attr('for')).length
        ) {
            // This will trigger a new click so we are out of here
            return;
        }

        // else do your thing here, it will not get called twice
    })
;

Comments

0

Yes, solved it by adding, https://www.w3schools.com/jsref/event_stopimmediatepropagation.asp

e.stopImmediatePropagation(); < - this stop multiple fires ⌚

$(document).on("click",".SaveGSTPayableAndRefund",function(e){
   e.stopImmediatePropagation();
})

Thanks :)

Comments

-2
$(document).on('click','.class',function() {
    alert('abc')
})

In my case it fired 2 times because I included this code 2 times like assets/js/test.js 2 times.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.