D3 Differentiate between click and drag for an element which has a drag behavior

I am unable to successfully distinguish between the click event and the drag event on an element that is bound to both using D3.js v3. The circle in the code below is assigned a drag behaviour and also a click listener. Demo here

var dragGroup = d3.behavior.drag()
    .on('dragstart', function () {
    console.log('Start Dragging Group');
})
    .on('drag', function (d, i) {
    d.x += d3.event.dx;
    d.y += d3.event.dy;
    d3.select(this).attr("transform", "translate(" + d.x + "," + d.y + ")");
});

var dragCircle = d3.behavior.drag()
    .on('dragstart', function () {
    d3.event.sourceEvent.stopPropagation();
    d3.event.sourceEvent.preventDefault();
    console.log('Start Dragging Circle');
})
    .on('drag', function (d, i) {
    d.cx += d3.event.dx;
    d.cy += d3.event.dy;
    d3.select(this).attr('cx', d.cx).attr('cy', d.cy);
});

var svg = d3.select('body').append('svg').attr('viewBox', '-50 -50 300 300');
var g = svg.selectAll('g').data([{
    x: 10,
    y: 10
}])
    .enter().append('g').call(dragGroup);

g.append('rect').attr('width', 100).attr('height', 100);

g.selectAll('circle').data([{
    cx: 90,
    cy: 80
}]).enter()
    .append('circle')
    .attr('cx', function (d) {
    return d.cx;
})
    .attr('cy', function (d) {
    return d.cy;
})
    .attr('r', 30)
    .call(dragCircle)
    .on('click', function () {
    console.log('clicked circle');
});

Whenever I click the circle in the example I get the console logging the drag event aswell as the click event. I also get the same behavior when dragging, first the drag event is logged and on mouseup the click event gets logged.

What is the correct way to handle these events separately?
The use case is an attempt to handle a node-click and a node-drag/drop in a tree layout.

Answers:

Answer

The key bit that's missing is the check whether the default behaviour of an event has been prevented. That is, there's a matching sibling to d3.event.preventDefault() -- d3.event.defaultPrevented. You need to check this in your click handler to see whether any dragging action is going on.

See also the answer to this question.

Answer

You can differentiate between a click and a dragstart, but it is harder to differentiate between a mousdown and a dragstart.

dragstart will be triggered when you begin your drag action, meaning when you do your mousedown. This is why. whenever you click, dragstart will be triggered. (a click is a mousedown + mouseup).

So preventing the click to be triggered should work. In your code, you should add the preventDefault as Lars Kotthoff has hinted. But don't put it in the dragstart function:

var dragCircle = d3.behavior.drag()
    .on('dragstart', function () {
    d3.event.sourceEvent.stopPropagation();
    d3.event.sourceEvent.preventDefault(); <-- Remove This
    console.log('Start Dragging Circle');
})

And add it on the right place (in the click function), and write it correctly with d3 (d3.event.defaultPrevented)

g.selectAll('circle').data([{
    cx: 90,
    cy: 80
}]).enter()
    .append('circle')
    .attr('cx', function (d) {
    return d.cx
})
    .attr('cy', function (d) {
    return d.cy
})
    .attr('r', 30)
    .call(dragCircle)
    .on('click', click);

function click(d) {
  if (d3.event.defaultPrevented) return; <-- Add d3.event.defaultPrevented
  console.log('clicked');
}

See the updated version. Now, when dragging, the click is not triggered anymore.

Keep in mind that when clicking, the dragstart is still triggered. (but not drag)

Answer

d3.event.sourceEvent.preventDefault() doesn't work as expected or rather its inconsistent.

I faced this issue and to differentiate between the two events, I used a boolean value isDragged inside the onDrag event. So if this value is set, the drag event is performed on the object if not then click event is performed. Also a normal click on object triggers its dragstart and dragend event but not onDrag event.

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.