Paul D'Ambra
Paul D'Ambra

Reputation: 7814

How to stop Angular handling a click event from within d3

I have an angular app in which clicking on an SVG element changes its state (in the attached snippet toggling it between fill colours)

I also have d3 wired up to drag the same object.

d3 documentation states that they stop the click event after a non-empty drag but whether or not I try to stop propagation ng-click is still picking up the click event.

Is it possible to have ng-click only be triggered if there has been no d3 drag?

In the attached snippet my expectation is that if you just click the blue rectangle it will toggle to yellow (and back again) but that if you drag it then the colour will not change. What I find is that on drag end the colour changes.

var myApp = angular.module('app', []);

myApp.controller('ctrl', function ($scope){
  //clicking rectangle toggles yellow background
  $scope.isYellow = false; 
  
  var drag = d3.behavior.drag()
        .on("drag", function(d,i) {
          var el = d3.select(this);
          var h = el.attr("height");
          var w = el.attr("width");
          var x = d3.event.x - (w/2);
          var y = d3.event.y - (h/2);
          el.attr("transform", "translate(" + [ x,y ] + ")")

          //try to stop the event from propagating
          d3.event.sourceEvent.stopPropagation();
          d3.event.preventDefault();
        });
  
  d3.select('rect').call(drag);
});
svg {background:red}
rect {fill:blue}
.yellow {fill: yellow}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

  <div ng-app="app">
    <svg width=250 height=250 ng-controller="ctrl">
      <rect 
            ng-class="{yellow : isYellow}"
            ng-click="isYellow = !isYellow"
            x="10" y="10" width="30" height="30">

      </rect>
    </svg>
  </div>

Upvotes: 3

Views: 807

Answers (1)

Paul D&#39;Ambra
Paul D&#39;Ambra

Reputation: 7814

As is so often the case just formulating the question in a spike gave my subconscious enough to work on.

The d3 docs even had the answer I just hadn't realised it

During a drag gesture, some browser default behaviors (such as text selection) are prevented. To allow the dragging of links, the default behavior for a click event that immediately follows a non-empty drag gesture is prevented. When registering your own click listener on draggable elements, you can check whether the click event was suppressed as follows:

selection.on("click", function() { if (d3.event.defaultPrevented) return; // click suppressed console.log("clicked!"); });

On passing Angular's $event in from ng-click allowed me to inspect isDefaultPrevented which is true after a d3 drag allowing a modified solution as in the snippet here

var myApp = angular.module('app', []);

myApp.controller('ctrl', function ($scope){
  //clicking rectangle toggles yellow background
  $scope.isYellow = false; 
  
  $scope.toggleColour = function($event) {
    if ($event.isDefaultPrevented()) {
     return;
    }
    $scope.isYellow = !$scope.isYellow;  
  };

  var drag = d3.behavior.drag()
        .on("drag", function(d,i) {
          var el = d3.select(this);
          var h = el.attr("height");
          var w = el.attr("width");
          var x = d3.event.x - (w/2);
          var y = d3.event.y - (h/2);
          el.attr("transform", "translate(" + [ x,y ] + ")")

          //try to stop the event from propagating
          d3.event.sourceEvent.stopPropagation();
          d3.event.preventDefault();
        });
  
  d3.select('rect').call(drag);
});
svg {background:red}
rect {fill:blue}
.yellow {fill: yellow}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

  <div ng-app="app">
    <svg width=250 height=250 ng-controller="ctrl">
      <rect 
            ng-class="{yellow : isYellow}"
            ng-click="toggleColour($event)"
            x="10" y="10" width="30" height="30">

      </rect>
    </svg>
  </div>

Upvotes: 5

Related Questions