I'm experiencing problems with the d3 drag behavior. Variations of the issue I'm having have been answered before, but I couldn't find an answer to my specific problem.
Here's a fiddle which illustrates the problem I'm going to describe.
What I want to do is to have a click handler on a draggable element where the click handler shouldn't be executed on dragend
. I know that inside the click handler I can use d3.event.defaultPrevented
which should be set to true if the element was dragged. The problem arises when the mouseup
event happens on another element than the mousedown
event. This happens when the movement of the dragged element is slower than the mouse cursor. If the mouse is released and the dragged element isn't under the mouse yet d3.event.defaultPrevented
is set to false and the click handler doesn't get called. This makes it impossible to find out whether the click
event was fired after a drag or not.
In my example the circle flashes green if the click handler executes but d3.event.defaultPrevented
was set to true. Also in the click handler propagation is prevented preventing the event to bubble up to the svg click handler. If the click handler of the circle doesn't execute and the event bubbles to the svg click handler the circle flashes blue if d3.event.defaultPrevented
was set to true otherwise it flashes red.
What I want to achieve is to get the circle to flash green or blue no matter where the circle is on mouse up in order to be able to know whether the click event happened after a drag or not. Is this even possible or is this a limitation by the nature of javascript/browsers? If so is there a workaround? Or do I just have to disable the 'slowing down' of the circle when it is dragged?
I found a very similar question on SO but there wasn't really a useful answer.
Any help appreciated!
EDIT Looks like the idea to stop the element from slowing down during dragging solves the problem. But I would still be interested if this is possible using the event information available.
Here's the code of the fiddle:
var nodes = [{}];
var svg = d3.select('body')
.append('svg')
.attr({
width: 500,
height: 500
})
.on('click', function(){
var color = d3.event.defaultPrevented ? 'blue' : 'red';
flashNode(color);
});
var force = d3.layout.force()
.size([500, 500])
.nodes(nodes)
.friction(.2)
.on('tick', forceTickHandler);
var nodeElements = svg
.selectAll('circle');
nodeElements = nodeElements
.data(force.nodes())
.enter()
.append('circle')
.attr({
cx: 10,
cy: 10,
r: 10
})
.on('click', function(){
d3.event.stopPropagation();
var color = d3.event.defaultPrevented ? 'green' : 'orange';
flashNode(color);
})
.call(force.drag);
function forceTickHandler(e){
nodes.forEach(function(node) {
var k = e.alpha * 1.4;
node.x += (250 - node.x) * k;
node.y += (250 - node.y) * k;
});
nodeElements
.attr('cx', function(d, i){
return d.x;
})
.attr('cy', function(d, i){
return d.y;
});
};
function flashNode(color){
nodeElements
.attr('fill', color)
.transition()
.duration(1000)
.attr('fill', 'black');
}
force.start();