dramasea
dramasea

Reputation: 3490

Why the onclick element will trigger twice for label element

      window.onload = function(){
         var wow = document.getElementById("wow");
    	 wow.onclick = function(){
    	     alert("hi");
    	 }
      }
    <label id="wow"><input type="checkbox" name="checkbox" value="value">Text</label>

This is my code, when I clicked on "Text" it will alert hi twice but when I clicked on the box, the onclick element will only trigger once, why?

Upvotes: 53

Views: 38889

Answers (8)

weiya ou
weiya ou

Reputation: 4338

Use change to listen for events instead of click

Upvotes: 1

jboxxx
jboxxx

Reputation: 747

For those using React and Material UI - I've encountered this issue on one of my forms. event.preventDefault() is stopping the event from bubbling as described by the accepted answer.

  <Button
    onClick={(event) => {
      event.preventDefault()
      this.handleCardClick(planName)
    }}
  >
    <FormControl component="fieldset">
      <RadioGroup
        row
        value={selectedPlan}
      >
        <FormControlLabel
          control={<Radio color="secondary" />}
          label={label}
        />
      </RadioGroup>
    </FormControl>
  </Button>

Upvotes: 1

Svenwas3f
Svenwas3f

Reputation: 65

This is probably the simplest answer. Just add a span around your text and stop event propagation.

window.onload = function(){
    var wow = document.getElementById("wow");
    wow.onclick = function(){
        alert("hi");
    }
}
<label id="wow">
    <input type="checkbox" name="checkbox" value="value">
    <span onclick="event.stopPropagation()">Text</span>
</label>

Or without inline-javaScript

window.onload = function(){
    var wow = document.getElementById("wow");
    wow.onclick = function(){
        alert("hi");
    }

    var span = document.getElementsByTagName("span")[0];
    span.onclick = function(event){
        event.stopPropagation();
    }
}
<label id="wow">
    <input type="checkbox" name="checkbox" value="value">
    <span>Text</span>
</label>

Upvotes: 0

Cran
Cran

Reputation: 1

Rammed headfirst into this gotcha again and decided to prove what it did to myself, hopefully helping me remember. To help future people to whom the above might not be clear enough, here the example I made.

https://jsfiddle.net/ashes/0bcauenm/

$(".someBigContainer").on("click", "input[type=checkbox]", ()=> {
    $output.val("Clicked: checkbox\n" + $output.val());
}); 

edit: added the link with a snipped of the code.

Upvotes: 0

RobG
RobG

Reputation: 147483

If your intention is to respond only to clicks on the label and not on the checkbox, you can look at the event.target property. It references the element that called the listener so that if the click wasn't on that element, don't to the action:

window.onload = function(){
  var el = document.getElementById('wow');
  el.addEventListener('click', function(event) {

    if (this === event.target) {
      /* click was on label */
      alert('click was on label');

    } else {
      /* click was on checkbox */
      event.stopPropagation();
      return false;
    }

  }, false);
}

If, on the other hand, you want to only respond to clicks on the checkbox (where a click on the label also produces a click on the checkbox), then do the reverse. Do nothing for clicks on the label and let ones from the checkbox through:

window.onload = function(){
  var el = document.getElementById('foolabel');
  el.addEventListener('click', function(event) {

    if (this === event.target) {
      /* click was on label */
      event.stopPropagation();
      return false;

    } else {

      /*
      ** click is from checkbox, initiated by click on label
      ** or checkbox                                  
      */

      alert('click from checkbox');
    }

  }, false);
}

This version seems to have the most natural behaviour. However, changing the markup so that the label no longer wraps the checkbox will mean the listener is not called.

Upvotes: 5

Andrew
Andrew

Reputation: 5340

The Label tag will be associated with the input tag inside it. So when you click the label, it will also trigger a click event for the input, then bubble to the label itself.

See this:

document.getElementById("winput").addEventListener('click', function(event){
     alert('input click');

     //stop bubble
     event.stopPropagation();

 }, false); 

http://jsfiddle.net/96vPP/

Upvotes: 1

Barmar
Barmar

Reputation: 782105

When you click on the label, it triggers the click handler, and you get an alert.

But clicking on a label also automatically sends a click event to the associated input element, so this is treated as a click on the checkbox. Then event bubbling causes that click event to be triggered on the containing element, which is the label, so your handler is run again.

If you change your HTML to this, you won't get the double alert:

<input id="wowcb" type="checkbox" name="checkbox" value="value">
<label id="wow" for="wowcb">Text</label>

The label is now associated with the checkbox using the for attribute instead of wrapping around it.

DEMO

Upvotes: 88

Tyler.z.yang
Tyler.z.yang

Reputation: 2450

Event bubble.

The checkbox is the child node of the label. You click the checkbox. Event bubble to the label. Then alert pop up twice.

To prevent alert pop up twice when you click the checkbox. You can change you onclick function into this:

wow.onclick = function(e){
    alert('hi');
    stopBubble(e);
}


function stopBubble(e)  
{  
    if (e && e.stopPropagation)  
        e.stopPropagation()  
    else 
        window.event.cancelBubble=true 
}  

Hope this can work for you.

Upvotes: 3

Related Questions