0

Can we bind ng-click to a CSS class?

I have loads of <td>s to bind and check if they are clicked then fire the click of the label in it, in order to give a maximum clickable area for touch devices.

I have done something like below.

var myApp = angular.module("myApp", [])
  .controller("myController", ["$scope", "$timeout", function myController($scope, $timeout) {
    $scope.checkRadio = function checkRadio($event) {      
      $timeout(function() {
        angular.element($event.target).find("label").trigger('click');
      }, 100);
    };
  }]);
.invisible-radio {
  position: absolute;
  display: inline;
  opacity: 0;
  width: 0;
  margin: 0;
  -webkit-appearance: none;
  overflow: hidden;
}

.custom-radio-label {
  display: inline-block;
  position: relative;
  padding-left: 20px;
  vertical-align: text-top;
  line-height: 20px;
  cursor: pointer;
  border-style: solid;
  border-width: 0;
}

.custom-radio-label::before {
  border-color: #7f7f7f;
  border-radius: 50%;
  background-color: #fff;
  border-width: 2px;
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  width: 18px;
  height: 18px;
  border-style: solid;
  -webkit-box-sizing: content-box;
  -moz-box-sizing: content-box;
  box-sizing: content-box;
}

.invisible-radio:checked+.custom-radio-label::after {
  background-color: #337ab7;
  border-radius: 50%;
  top: 5px;
  left: 5px;
  content: "";
  width: 12px;
  height: 12px;
  position: absolute;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<table class="table table-hover text-center center-block" ng-app="myApp" ng-controller="myController">
  <thead>
    <tr>
      <th>Option 1</th>
      <th>Option 2</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td ng-click="checkRadio($event)">
        <input id="option1" type="radio" class="invisible-radio" name="option" value="0" ng-model="model.Option" required />
        <label class="custom-radio-label" for="option1"></label>
      </td>
      <td ng-click="checkRadio($event)">
        <input id="option2" type="radio" class="invisible-radio" name="option" value="1" ng-model="model.Option" required />
        <label class="custom-radio-label" for="option2"></label>
      </td>
    </tr>
  </tbody>
</table>

But it seems overkill to go and decorate 100s of <td>s with ng-click attribute.

I am thinking if it is possible to bind ng-click to a common class that all my <td>s share.

It is certainly possible to do it with jQuery as below.

$(".someclassOnMyTD").click($event) {
   $event.target.find("label").click();
}

But I am looking for an Angular way here. A directive possibly.

4
  • Are those 100 td's not generated in any way using a loop? Commented Jun 8, 2017 at 3:59
  • @Icycool, nope! That's the tragedy! :( Otherwise, this question would not have existed. :) So I am going to try Mr_Perfect's answer if it works. As yours is an excellent suggestion, however, it will require me updating HTML for all those tds. Commented Jun 8, 2017 at 4:31
  • well you would have to do that for directive as well, like <td click-me>. imo using jquery in angular when necessary is fine. Commented Jun 8, 2017 at 4:34
  • Totally agree there with you @Icycool! I think using the best features of both jQuery and AngularJs is perfectly fine in situations like this. Commented Jun 8, 2017 at 4:45

3 Answers 3

3

You can do it by using angular.element

  var cells = angular.element(document.querySelectorAll(".someclassOnMyTD"));
  cells.on('click', function(event){
        // write your code here
        // var labels = angular.element(event.currentTarget).find('label');
  })
Sign up to request clarification or add additional context in comments.

1 Comment

I went on to use your solution and built my requirement on top of it. Certainly gave me a start!
1

Actually that is what a <label> for.

<td>
  <label for="option1">
    <input id="option1" type="radio" class="invisible-radio" name="option" value="0" ng-model="model.Option" required />
    <label class="custom-radio-label" for="option1"></label>
  </label>
</td>

You might need to tinker with the CSS a little so it fills the area of its parent td

Comments

0

I was using ng-view and needed to bind the event to a lot of tds in almost all of my (more than 20) views.

Here's complete solution as it might turn useful to someone with the similar requirement as mine, in future.

$scope.$on('$viewContentLoaded', function () {
    var tables = angular.element(document.querySelectorAll(".myClassOnTable, .myOtherClassOnTable"));
    angular.forEach(tables, function (table) {
        angular.element(table).on("click", function (e) {
            var targetElement = e.target;
            if (targetElement.nodeName == "TD" || targetElement.nodeName == "td") {
                var radio = angular.element(targetElement).find(".invisible-radio");
                if (radio) {
                    angular.element(radio[0]).prop("checked", true);
                    // I needed to set the required attribute manually as I am setting the checked property manually
                    $scope.myForm[radio[0].name].$setValidity("required", true);
                    $scope.$apply();
                }
            }
        });
    });
});

Notice that I have tied the click event to the table element and I am listening if the target of the click is a td. This is because I was running into below error due to a browser limitation to bind click events to hundreads of elements at the same time.

Uncaught RangeError: Maximum call stack size exceeded

So I am delegating the click event to a lesser number of elements.

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.