Your two code examples look identical, so it's a bit difficult to say
precisely what the problem is. But, I suspect the confusion is related
to method chaining.
If you want to render an svg:circle and an svg:text for each node,
then you should map the nodes to svg:g elements. The `g` element is
SVG's container element, much like the `div` in HTML. So you do
something like this:
var node = vis.selectAll("g.node")
.data(followers.nodes)
.enter().append("svg:g")
.attr("class", "node")
.attr("transform", function(d) { return "translate(" + d.x + ","
+ d.y + ")"; });
OK, now your `node` refers to a container, to which you can add both
circles and text. But make sure you add the text to the g container,
not the circle. When you use append, it returns the selection you just
added! So, you need to keep a reference to the parent if you want to
create siblings rather than children. For example:
node.append("svg:circle")
.attr("r", 7)
.style("fill", "#234B6F")
.call(force.drag);
node.append("svg:text")
.style("pointer-events", "none")
.text(function(d) { return d.data.key; });
Mike
You need to bind the .call(force.drag) to the svg:g element (that you
are transforming), rather than the child circle. At least, that's my
guess. Alternatively, what version of D3 are you using? I fixed a bug
related to this fairly recently. Working example here:
Glad you're enjoying D3!
Mike
There's a soft bounding box currently, by way of a "gravity" force
that pulls each node towards the center of the layout. It's actually
more like an invisible spring that connects each node to the center,
such that the center pull gets stronger the farther away the node is
(and has little effect in the middle, so as to avoid distortion).
Depending on the size and connectivity of your graph, you may need to
play with the different parameters so that the graph stays
approximately within its bounds; two things to try are decreasing the
charge force and increasing the gravity force. You can do this from
the developer console to see how it affects your graph interactively!
If you want to implement a proper hard bounding box, you can do that
in the on("tick") callback; just set the `x` and `y` attributes of
your nodes so they're within the bounds. D3 uses position Verlet
integration, which means the simulation automatically adjusts to
repositioning of nodes.
Mike
Hello all,
A similar piece of code always gives the error:
node.exit is not a function
var node = vis.selectAll("g.node")
.data(nodes, function(d) { return d.id; })
.enter().append("svg:g")
.attr("class", "node")
.call(force.drag)
node.append("svg:circle")
.attr("class", "node")
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
.attr("r", 5)
.style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; })
.call(force.drag)
node.append("svg:text")
.attr("class", "nodetext")
.attr("dx", function(d) { return d.x; })
.attr("dy", function(d) { return d.y; })
.text(function(d) { return d.name })
.call(force.drag)
node.exit().remove();I guess this is because I couldn't select the nodes directly when they are
displayed on a container g.
So how do we select the nodes or the texts displayed on a container g,
so that we can remove them both... (so that I could call node.exit().remove())?
Best regards,
neotrex
This is a fairly common mistake I think. Note that your variable "node"
above is actually the result of calling .enter(). So you can't call
.exit() on it. The fix is:
var node = vis.selectAll("g.node")
.data(...);
var nodeEnter = node.enter().append("svg:g")
.attr(...)
.call(...);
nodeEnter.append(...);
node.exit().remove();
Note that you don't currently have any code to deal with the update
selection. So you would need to add something like:
// Update existing nodes
node.select("circle")
.attr("cx", ...);
We may get a change soon to make this simpler:
<https://github.com/mbostock/d3/pull/184> but it's not been released
yet. This change would make it easier to update both the enter and
update selection in one go, because the update selection would be
automatically updated with anything appended to the enter selection.
Hope that makes sense,
--
Jason Davies, http://www.jasondavies.com/
Here you are binding the click handler only to the update selection,
which explains why it's not working. I think you want to bind to the
enter selection instead.
Why are you setting the "cx" and "cy" attributes for your text node?
You probably want "dx" and "dy". Also you're missing other attributes
that you were setting for your enter selection, such as "fill", as well
as calling .text(), assuming you want to update those too.
If you are going to put the circle and text inside a g element, it's
easier to move the g element using the "transform" attribute rather
than moving the circle and text individually.
Also, the definition of `node` in your code is the updating selection,
so if you say node.select("circle").attr(…) in your tick handler,
you're only touching the updating nodes. So probably you need to
re-select to combine enter and update at the end of your
initialization:
// reselect
node = vis.selectAll("g.node");
(Or, call vis.selectAll("g.node") in your tick handler.)
Also, thanks Jason for fielding questions. :)
Mike
You could debug this using your browser's developer console. The
questions to ask:
1. Is your code being run?
Try setting a breakpoint or using console.log. This will verify that
the "tick" event listener is being called at the expected times.
Setting a breakpoint is a bit more reliable, but you can also call a
method that doesn't exist (e.g., `fail()`) to cause an error that
opens the debugger.
2. Does your selection contain the expected elements?
If you console.log the selection, you'll be able to open the contained
arrays and see what elements are selected. For example:
console.log(node);
If the selection doesn't contain the expected elements, perhaps you
need to reselect or there's a typo in the selector.
3. Do your attributes have the expected values?
Using the element inspector, you can look at individual text, circle
and g elements to see what their attributes are. You can even edit the
attributes directly from the inspector to see if changing the values
affects the elements as you expect. If the attributes look correct in
the element inspector, but don't look right in the visualization, then
consult the SVG specification or other examples for guidance.
Mike