0

I am relatively new to d3, and I'm still bumping into things that stump me.

Basically, I have a tree with draggable nodes. I want to restrict the "drag handle" of the nodes to only the circle, not the circle and text (and whatever else I end up adding).

I know that it has something to do with properly setting the origin in my drag behavior, but I just can't quite seem to get it.

I've found some useful information here on stackoverflow (sorry, I can only post two links), and looking at https://groups.google.com/forum/#!topic/d3-js/fjp1mpvVW1M it appears that I'm applying the drag behavior to a child element of a g container and then trying to apply the transform to its g container.

How can I accomplish this?

here's the relevant code:

var dragListener = d3.behavior.drag()
.origin(function () {
    var t = d3.select(this);
    return {
        x: t.attr("x") + d3.transform(t.attr("transform")).translate[0],
        y: t.attr("y") + d3.transform(t.attr("transform")).translate[1]
    };
})
.on("drag", function (d) {
    d.x0 += d3.event.dy;
    d.y0 += d3.event.dx;       
    //var node = d3.select(this);  //this works
    var node = d3.select(this.parentNode)  //this doesn't work
    node.attr("transform", "translate(" + d.y0 + "," + d.x0 + ")");
});

  var nodeEnter = node.enter().append("g")
  //.call(dragListener)  // this works
  .attr("class", "node")
  .attr("transform", function(d) { return "translate(" + source.y0 + "," + source.x0 + ")"; })
  .on("click", click);

  nodeEnter.append("circle")
  .call(dragListener)  // this doesn't work
  .attr("r", 1e-6)
  .style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });

Here's a fiddle: http://jsfiddle.net/tsmith77/4R3Cb/

UPDATE

I wasn't able to get the child group to do what I wanted, but I accomplished what I want by intercepting the mousedown event on the text:

  nodeEnter.append("text")
  .on("mousedown", function (d) {
      d3.event.stopPropagation();
      })
  .on("click", function (d) {
      alert("text clicked");
  })

This prevents the text from being a drag handle, but still allows me to perform an action when the text is clicked.

tshepang
  • 12,111
  • 21
  • 91
  • 136
Tony
  • 9
  • 2
  • You're running into trouble here because you're using two different ways of setting the position (`transform` and setting coordinates for the element) and it's quite easy to get confused there. Are you looking for something like http://jsfiddle.net/4R3Cb/3/ – Lars Kotthoff Jul 11 '14 at 17:02
  • Thanks Lars. Yes, something like that. Only when dragging the circle I would want the whole node to travel. My only desired difference in behavior between my //this works and my //this doesn't work code is for the circle to be the only element that my user can "grab" for dragging. – Tony Jul 11 '14 at 20:04
  • Well it looks like in your example this is already the case? At least I can't drag the text in that. – Lars Kotthoff Jul 11 '14 at 20:25
  • I'm sorry for failing to communicate my desire. I want the whole node (both text and circle) to move when I drag, but I want the circle to be the only thing you can "grab" with your mouse. The reason is that I have a click event attached to the text, and i don't want the user accidentally firing that when they just wanted to drag. – Tony Jul 11 '14 at 21:54
  • That's exactly the behaviour I'm getting in your jsfiddle, modulo some jitter for which [this question](http://stackoverflow.com/questions/10988445/d3-behavior-zoom-jitters-shakes-jumps-and-bounces-when-dragging) should be useful. – Lars Kotthoff Jul 12 '14 at 07:46

1 Answers1

1

Set up following line in your CSS for text correspondent to nodes:

  pointer-events: none;

(one way to do it would be to assign a class named let's say "node-label" to all such texts, and then, to define that class' property "pointer-events" as above)

This will prevent dragging by grabbing text.

I'll give you also two live examples. They deal with force layout, not tree like in your case, but it shouldn't matter, the principle is the same.

example 1 - dragging possible by grabbing both circles and labels

example 2 - dragging possible by grabbing only circles

The difference between them is only the line:

  pointer-events: none;
VividD
  • 10,456
  • 6
  • 64
  • 111
  • That would make it wonderfully easy, but unfortunately I have a click event attached to the text. Basically I want the text to be my click element and my circle to be my drag and collapse element. – Tony Jul 11 '14 at 21:55
  • 1
    You should have stated that in your question. Please see (http://stackoverflow.com/help/mcve). – VividD Jul 12 '14 at 05:55