1

I cannot figure out that in this demo, why clicking label triggers sliding animation twice, while clicking check box triggers sliding animation once. Can someone explain this? Thanks!

I attach the code here.

<div ng-app="labelApp" ng-controller="labelCtrl">
  <label ng-click="toggle()">
    <input type="checkbox">Click to toggle slide</label>
  <div id="myDiv">
    Clicking check box triggers sliding animation once. 
    <br>
    Clicking label triggers sliding animation twice.
  </div>
</div>

angular.module("labelApp", [])
  .controller("labelCtrl", function($scope) {
    $scope.toggle = function() {
      $("#myDiv").slideToggle();
    }
  });
6324
  • 4,678
  • 8
  • 34
  • 63
  • 1
    You are getting two click events. Clicking the label also clicks the input which bubbles back up to the label. If you just put the click handler on the `input` instead of the `label` it should work. – Matt Burland Jun 07 '16 at 20:37
  • @MattBurland Yes, it works! Thanks so much! – 6324 Jun 07 '16 at 20:39
  • You can also use flag systems to as well. That has always been helpful when I have complex animations. The other side of the coin here is, why not use CSS transition animations rather than coding it? – Cam Jun 07 '16 at 20:40
  • @leroydev The slideToggle animation will execute twice. Matt pointed it out. :) – 6324 Jun 07 '16 at 20:42
  • @Cam Thanks for your comment. I am not good with CSS so I use jQuery UI. Matt has the solution for this problem. :) – 6324 Jun 07 '16 at 20:44

2 Answers2

4

When you click the label the event will tunnel down to the input where it will cause the checkbox to be checked and then it will bubble back up to the label again. So your handler on the label gets fired twice. The quick fix is to just put the handler on the input instead:

<label>
    <input ng-click="toggle()" type="checkbox">Click to toggle slide
</label>

But probably more appropriate would be to use ng-change:

<input ng-change="toggle()" ng-model="isChecked" type="checkbox">Click to toggle slide
Matt Burland
  • 44,552
  • 18
  • 99
  • 171
  • Thanks Matt. You are so helpful! Could you briefly explain why `ng-change` is better than `ng-click` here? – 6324 Jun 07 '16 at 20:49
  • Mostly because of semantics. If the thing you are actually interested in is the *change* of the checkbox from checked to not checked and back again, then you are interested in the `change` event not the `click` event. It maybe a click that caused the change, but that's beside the point. – Matt Burland Jun 07 '16 at 20:52
  • Got it. Thanks for your explanation. :) – 6324 Jun 07 '16 at 20:53
0

When a child element is triggered, the event bubbles up and all parent elements are triggered as well unless propagation is stopped.

Labels with enclosed inputs have the property of triggering a click event for the input they are attached to as well as themselves, so clicking the label triggers the event for itself and for its attached input. In this case the attached input is its child, so when it is triggered it bubbles back up to the parent which is the label. So in effect the label is triggering itself a second time.

You can see how a detached label can trigger an input's click event here: https://jsfiddle.net/yosefh/14qtas7q/ and ways to get around it here https://stackoverflow.com/a/30810281/6419701

To solve your problem, you can stop propagation on the input by adding ng-click="$event.stopPropagation()" to the checkbox. But if you did that then the event would only be triggered by the label and not the checkbox.

Incidentally, if you change the label element to a div element you will not have this problem.

Although semantically, it is better to use a label for the purpose of a label rather than turn it into a div.

So you can simply move ng-click to the checkbox as I've done here

<div ng-app="labelApp" ng-controller="labelCtrl">
  <label>
    <input type="checkbox" ng-click="toggle()">Click to toggle slide
  </label>
  <div id="myDiv">
    Clicking check box triggers sliding animation once. 
    <br>
    Clicking label triggers sliding animation twice.
  </div>
</div>

https://jsfiddle.net/yosefh/9uf9vpyk/

You can see a similar problem here: Angular.js ng-click events on labels are firing twice

If you have issues with event propagation in general you can see this post https://stackoverflow.com/a/15193650/6419701

Community
  • 1
  • 1
yosefrow
  • 2,128
  • 20
  • 29