Drag and Drop Example

6,599 views
Skip to first unread message

Christopher Cliff

unread,
Aug 22, 2011, 11:59:16 PM8/22/11
to d3-js
It's not clear to me how to leverage d3 for drag and drop
interactions. Are there any simple examples floating around?

Chris Viau

unread,
Sep 5, 2011, 3:29:14 AM9/5/11
to d3...@googlegroups.com
You can see how to use d3.behavior.drag() by looking at the source code of the force-directed layout in d3.layout.js:

  // use `node.call(force.drag)` to make nodes draggable
  force.drag = function() {
    if (!drag) drag = d3.behavior.drag()
        .on("dragstart", dragstart)
        .on("drag", d3_layout_forceDrag)
        .on("dragend", d3_layout_forceDragEnd);

    this.on("mouseover.force", d3_layout_forceDragOver)
        .on("mouseout.force", d3_layout_forceDragOut)
        .call(drag);
  };

But a simpler example would be great.

Jason Davies

unread,
Sep 5, 2011, 4:19:53 AM9/5/11
to d3...@googlegroups.com

There is a tiny gotcha when using d3.behavior.drag, which is that you
need to store the drag origin if you want to restrict the dragged
element to a particular area. A simple example is in:

http://www.jasondavies.com/morley-triangle/

The relevant code is:

var circle = vis.selectAll("circle")
.data(ps);
circle.enter().append("svg:circle")
.attr("r", 7.5)
.call(d3.behavior.drag()
.on("dragstart", function(d) {
this.__origin__ = d.slice();
})
.on("drag", function(d) {
d[0] = Math.max(0, Math.min(this.__origin__[0] += d3.event.dx, w));
d[1] = Math.max(0, Math.min(this.__origin__[1] += d3.event.dy, h));
update();
})
.on("dragend", function() {
delete this.__origin__;
}));
circle
.attr("cx", function(d) { return d[0]; })
.attr("cy", function(d) { return d[1]; });

It would be nice if d3.behavior.drag did something like this automatically, but
I haven't come up with an elegant solution yet. Perhaps an optional "origin"
accessor?

If you don't care about restricting the position, you can drop the "dragstart"
and "dragend" listeners and just do:

d[0] += d3.event.dx;
d[1] += d3.event.dy;

--
Jason Davies, http://www.jasondavies.com/

Chris Viau

unread,
Sep 5, 2011, 10:38:59 AM9/5/11
to d3...@googlegroups.com
I'm slowly converting and old javascript drag and drop code to integrate more tightly with D3. Maybe it can help even if I'm in the middle of the conversion process. I simply bind the events on the root SVG and use the transform attribute to translate the dragged element. Maybe it will turn out to be a very dumb version of what D3 already provides?

http://bl.ocks.org/1195063

Davin

unread,
Sep 6, 2011, 3:53:14 AM9/6/11
to d3...@googlegroups.com
I also wrote my own drag code and am trying to convert to use d3's drag module.  However, I'm having trouble because the "drag" event seems to switch signs on the d3.event.dx value for each consecutive event fire even if I'm dragging in only one direction.  I'd at least expect the numbers to have the same sign when dragging in one direction.  Here's my code:

vis.append("svg:rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", 100)
.attr("height", 100)
.call(d3.behavior.drag()
    .on("drag", function(d) { console.log("d3.event.dx=" + d3.event.dx); });

results in the following output when dragged left:

d3.event.dx=-1
d3.event.dx=-4
d3.event.dx=5
d3.event.dx=-7
d3.event.dx=6
d3.event.dx=-8
d3.event.dx=7

Is there anything wrong here that causes the d3.event.dx to keep switching signs?

Thanks!

            

Jason Davies

unread,
Sep 6, 2011, 4:16:50 AM9/6/11
to d3...@googlegroups.com

Your code as given above works fine for me. Note that the mouse
position is computed relative to the dragged element's parent container,
so if you are moving the container when dragging, this could cause the
behaviour you're seeing. Perhaps you could post a working example if
you're still stuck?

Mike Bostock

unread,
Sep 6, 2011, 11:50:05 AM9/6/11
to d3...@googlegroups.com
> so if you are moving the container when dragging,

Specifically, if you are applying a "transform" attribute to the same
element to which you applied the drag behavior, bad things will
happen. If you're setting x & y attributes, you should be okay, or if
you assign the drag behavior to an enclosing svg:g element instead.

Mike

Davin

unread,
Sep 6, 2011, 7:40:38 PM9/6/11
to d3...@googlegroups.com
Yup, that was exactly it.  I applied the drag behavior to a child element of a g container and then applied the transform to that g container.  I switched to using x & y attributes and it works great now.  It could be nice to be able to pass in as an argument which element to use as the dragPoint.  

I also experienced some weirdness on mobile safari because it doesn't seem like the dragend function is executing.  I'm looking into that.

Davin
Reply all
Reply to author
Forward
0 new messages