Reputation: 7814
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
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