Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to prevent d3 from triggering drag on right click?

Tags:

d3.js

I have defined some drag behaviour that works as expected as follows (code in CoffeeScript):

nodeDrag = d3.behavior.drag()
  .on("dragstart", (d, i) ->
    force.stop())
  .on("drag", (d, i) ->
    d.px += d3.event.dx
    d.py += d3.event.dy
    d.x += d3.event.dx
    d.y += d3.event.dy
    tick())
  .on("dragend", (d, i) -> 
    force.resume()
    d.fixed = true
    tick())

// ...

nodes = vis.selectAll(".node")
  .data(graph.nodes)
  .enter()
    .append("g")
    // ...
    .call(nodeDrag)

I now try to create custom behaviour for right clicks on nodes. However, this triggers "dragstart" and "drag", i.e. after I call e.preventDefault() on the "contextmenu" event, the node in question is stuck to my mouse pointer and follows it around until I do another (left) click to force a release (I assume e.preventDefault() also causes "dragend" to never fire).

I found a brief discussion of this issue in a thread on Google Groups and a discussion in d3's issues on Github. However, I cannot figure out from those comments how to prevent this behaviour.

How can I not trigger dragging on right click?

like image 486
notan3xit Avatar asked Jun 14 '13 13:06

notan3xit


4 Answers

I found a possibility to limit the drag gestures to left mouse button only.

It involves an additional field that records when a gesture has been initiated:

dragInitiated = false

The rest of the code is then modified to register initiation and termination of a desired drag gestures on "dragstart" and "dragend", respectively. Actions for "drag" are then only performed if a drag gestures was properly initiated.

nodeDrag = d3.behavior.drag()
  .on "dragstart", (d, i) ->
    if (d3.event.sourceEvent.which == 1) # initiate on left mouse button only
      dragInitiated = true               # -> set dragInitiated to true
      force.stop()
  .on "drag", (d, i) ->
    if (dragInitiated)                   # perform only if a drag was initiated
      d.px += d3.event.dx
      d.py += d3.event.dy
      d.x += d3.event.dx
      d.y += d3.event.dy
      tick()
  .on "dragend", (d, i) ->
    if (d3.event.sourceEvent.which == 1) # only take gestures into account that
      force.resume()                     # were valid in "dragstart"
      d.fixed = true
      tick()
      dragInitiated = false              # terminate drag gesture

I am not sure whether this is the most elegant solution, but it does work and is not exceptionally clumsy or a big hack.

like image 103
notan3xit Avatar answered Nov 02 '22 18:11

notan3xit


for D3 v4 see drag.filter([filter])

If filter is specified, sets the filter to the specified function and returns the drag behavior. If filter is not specified, returns the current filter, which defaults.

drag.filter([filter])

for the right click:

.filter(['touchstart'])

Filters available

mousedown, mousemove, mouseup, dragstart, selectstart, click, touchstart, touchmove, touchend, touchcancel

like image 33
Rifton007 Avatar answered Nov 02 '22 19:11

Rifton007


Little late to the party, I had the same problem and I used the following method to make sure my drag only works for left click.

 var drag = d3.behavior.drag()

            .on('drag', function () {

            console.log(d3.event.sourceEvent.button);

            if(d3.event.sourceEvent.button == 0){
                var mouse = d3.mouse(this);
                d3.select(this)
                        .attr('x', mouse[0])
                        .attr('y', mouse[1]);
            }

        });
like image 1
Fawzan Avatar answered Nov 02 '22 18:11

Fawzan


To handle all listeners of drag event, you can use the code below:

function dragChartStart() {
    if(d3.event.sourceEvent.button !== 0) {
        console.log("not left click");
        return;
    }
    console.log("dragStart");
}

function dragChartEnd() {
    if(d3.event.sourceEvent.button !== 0) {
        console.log("not left click");
        return;
    }
    console.log("dragEnd");
}

function dragChartMove() {
    if(d3.event.sourceEvent.button !== 0) {
        console.log("not left click");
        return;
    }
    console.log("dragMove");
}

var dragBehavior = d3.behavior.drag()
    .on("drag", dragChartMove)
    .on("dragstart", dragChartStart)
    .on("dragend", dragChartEnd);
like image 1
paradite Avatar answered Nov 02 '22 18:11

paradite