D3 V4 Draggable network with Force Directed Layout - javascript
What am trying to do is
Have user select a bunch of nodes
Let the user drag those nodes
Use forcesimulation to manage the layout.
Something similar to http://jsfiddle.net/pkerpedjiev/29majy5c/2/
But I need this in V4.
var graph = {"nodes":[{"x":444,"y":275},{"x":378,"y":324},{"x":478,"y":278},{"x":471,"y":256},{"x":382,"y":269},{"x":371,"y":247},{"x":359,"y":276},{"x":364,"y":302},{"x":400,"y":330},{"x":388,"y":298},{"x":524,"y":296},{"x":570,"y":243},{"x":552,"y":159},{"x":502,"y":287},{"x":511,"y":313},{"x":513,"y":265},{"x":602,"y":132},{"x":610,"y":90},{"x":592,"y":91},{"x":575,"y":89},{"x":607,"y":73},{"x":591,"y":68},{"x":574,"y":73},{"x":589,"y":149},{"x":620,"y":205},{"x":621,"y":230},{"x":589,"y":234},{"x":602,"y":223},{"x":548,"y":188},{"x":532,"y":196},{"x":548,"y":114},{"x":575,"y":174},{"x":497,"y":250},{"x":576,"y":196},{"x":504,"y":201},{"x":494,"y":186},{"x":482,"y":199},{"x":505,"y":219},{"x":486,"y":216},{"x":590,"y":306},{"x":677,"y":169},{"x":657,"y":258},{"x":667,"y":205},{"x":552,"y":227},{"x":518,"y":173},{"x":473,"y":125},{"x":796,"y":260},{"x":731,"y":272},{"x":642,"y":288},{"x":576,"y":269},{"x":605,"y":187},{"x":559,"y":289},{"x":544,"y":356},{"x":505,"y":365},{"x":579,"y":289},{"x":619,"y":282},{"x":574,"y":329},{"x":664,"y":306},{"x":627,"y":304},{"x":643,"y":327},{"x":664,"y":348},{"x":665,"y":327},{"x":653,"y":317},{"x":650,"y":338},{"x":622,"y":321},{"x":633,"y":338},{"x":647,"y":357},{"x":718,"y":362},{"x":636,"y":240},{"x":640,"y":227},{"x":617,"y":249},{"x":631,"y":254},{"x":566,"y":213},{"x":713,"y":322},{"x":716,"y":298},{"x":666,"y":241},{"x":627,"y":355}],"links":[{"source":1,"target":0},{"source":2,"target":0},{"source":3,"target":0},{"source":3,"target":2},{"source":4,"target":0},{"source":5,"target":0},{"source":6,"target":0},{"source":7,"target":0},{"source":8,"target":0},{"source":9,"target":0},{"source":11,"target":10},{"source":11,"target":3},{"source":11,"target":2},{"source":11,"target":0},{"source":12,"target":11},{"source":13,"target":11},{"source":14,"target":11},{"source":15,"target":11},{"source":17,"target":16},{"source":18,"target":16},{"source":18,"target":17},{"source":19,"target":16},{"source":19,"target":17},{"source":19,"target":18},{"source":20,"target":16},{"source":20,"target":17},{"source":20,"target":18},{"source":20,"target":19},{"source":21,"target":16},{"source":21,"target":17},{"source":21,"target":18},{"source":21,"target":19},{"source":21,"target":20},{"source":22,"target":16},{"source":22,"target":17},{"source":22,"target":18},{"source":22,"target":19},{"source":22,"target":20},{"source":22,"target":21},{"source":23,"target":16},{"source":23,"target":17},{"source":23,"target":18},{"source":23,"target":19},{"source":23,"target":20},{"source":23,"target":21},{"source":23,"target":22},{"source":23,"target":12},{"source":23,"target":11},{"source":24,"target":23},{"source":24,"target":11},{"source":25,"target":24},{"source":25,"target":23},{"source":25,"target":11},{"source":26,"target":24},{"source":26,"target":11},{"source":26,"target":16},{"source":26,"target":25},{"source":27,"target":11},{"source":27,"target":23},{"source":27,"target":25},{"source":27,"target":24},{"source":27,"target":26},{"source":28,"target":11},{"source":28,"target":27},{"source":29,"target":23},{"source":29,"target":27},{"source":29,"target":11},{"source":30,"target":23},{"source":31,"target":30},{"source":31,"target":11},{"source":31,"target":23},{"source":31,"target":27},{"source":32,"target":11},{"source":33,"target":11},{"source":33,"target":27},{"source":34,"target":11},{"source":34,"target":29},{"source":35,"target":11},{"source":35,"target":34},{"source":35,"target":29},{"source":36,"target":34},{"source":36,"target":35},{"source":36,"target":11},{"source":36,"target":29},{"source":37,"target":34},{"source":37,"target":35},{"source":37,"target":36},{"source":37,"target":11},{"source":37,"target":29},{"source":38,"target":34},{"source":38,"target":35},{"source":38,"target":36},{"source":38,"target":37},{"source":38,"target":11},{"source":38,"target":29},{"source":39,"target":25},{"source":40,"target":25},{"source":41,"target":24},{"source":41,"target":25},{"source":42,"target":41},{"source":42,"target":25},{"source":42,"target":24},{"source":43,"target":11},{"source":43,"target":26},{"source":43,"target":27},{"source":44,"target":28},{"source":44,"target":11},{"source":45,"target":28},{"source":47,"target":46},{"source":48,"target":47},{"source":48,"target":25},{"source":48,"target":27},{"source":48,"target":11},{"source":49,"target":26},{"source":49,"target":11},{"source":50,"target":49},{"source":50,"target":24},{"source":51,"target":49},{"source":51,"target":26},{"source":51,"target":11},{"source":52,"target":51},{"source":52,"target":39},{"source":53,"target":51},{"source":54,"target":51},{"source":54,"target":49},{"source":54,"target":26},{"source":55,"target":51},{"source":55,"target":49},{"source":55,"target":39},{"source":55,"target":54},{"source":55,"target":26},{"source":55,"target":11},{"source":55,"target":16},{"source":55,"target":25},{"source":55,"target":41},{"source":55,"target":48},{"source":56,"target":49},{"source":56,"target":55},{"source":57,"target":55},{"source":57,"target":41},{"source":57,"target":48},{"source":58,"target":55},{"source":58,"target":48},{"source":58,"target":27},{"source":58,"target":57},{"source":58,"target":11},{"source":59,"target":58},{"source":59,"target":55},{"source":59,"target":48},{"source":59,"target":57},{"source":60,"target":48},{"source":60,"target":58},{"source":60,"target":59},{"source":61,"target":48},{"source":61,"target":58},{"source":61,"target":60},{"source":61,"target":59},{"source":61,"target":57},{"source":61,"target":55},{"source":62,"target":55},{"source":62,"target":58},{"source":62,"target":59},{"source":62,"target":48},{"source":62,"target":57},{"source":62,"target":41},{"source":62,"target":61},{"source":62,"target":60},{"source":63,"target":59},{"source":63,"target":48},{"source":63,"target":62},{"source":63,"target":57},{"source":63,"target":58},{"source":63,"target":61},{"source":63,"target":60},{"source":63,"target":55},{"source":64,"target":55},{"source":64,"target":62},{"source":64,"target":48},{"source":64,"target":63},{"source":64,"target":58},{"source":64,"target":61},{"source":64,"target":60},{"source":64,"target":59},{"source":64,"target":57},{"source":64,"target":11},{"source":65,"target":63},{"source":65,"target":64},{"source":65,"target":48},{"source":65,"target":62},{"source":65,"target":58},{"source":65,"target":61},{"source":65,"target":60},{"source":65,"target":59},{"source":65,"target":57},{"source":65,"target":55},{"source":66,"target":64},{"source":66,"target":58},{"source":66,"target":59},{"source":66,"target":62},{"source":66,"target":65},{"source":66,"target":48},{"source":66,"target":63},{"source":66,"target":61},{"source":66,"target":60},{"source":67,"target":57},{"source":68,"target":25},{"source":68,"target":11},{"source":68,"target":24},{"source":68,"target":27},{"source":68,"target":48},{"source":68,"target":41},{"source":69,"target":25},{"source":69,"target":68},{"source":69,"target":11},{"source":69,"target":24},{"source":69,"target":27},{"source":69,"target":48},{"source":69,"target":41},{"source":70,"target":25},{"source":70,"target":69},{"source":70,"target":68},{"source":70,"target":11},{"source":70,"target":24},{"source":70,"target":27},{"source":70,"target":41},{"source":70,"target":58},{"source":71,"target":27},{"source":71,"target":69},{"source":71,"target":68},{"source":71,"target":70},{"source":71,"target":11},{"source":71,"target":48},{"source":71,"target":41},{"source":71,"target":25},{"source":72,"target":26},{"source":72,"target":27},{"source":72,"target":11},{"source":73,"target":48},{"source":74,"target":48},{"source":74,"target":73},{"source":75,"target":69},{"source":75,"target":68},{"source":75,"target":25},{"source":75,"target":48},{"source":75,"target":41},{"source":75,"target":70},{"source":75,"target":71},{"source":76,"target":64},{"source":76,"target":65},{"source":76,"target":66},{"source":76,"target":63},{"source":76,"target":62},{"source":76,"target":48},{"source":76,"target":58}]}
var width = 400,
height = 500,
shiftKey;
var svg = d3.select("body")
.attr("tabindex", 1)
.on("keydown.brush", keydown)
.on("keyup.brush", keyup)
.each(function() { this.focus(); })
.append("svg")
.attr("width", width)
.attr("height", height);
var link = svg.append("g")
.attr("class", "link")
.selectAll("line");
var brush = svg.append("g")
.datum(function() { return {selected: false, previouslySelected: false}; })
.attr("class", "brush");
var node = svg.append("g")
.attr("class", "node")
.selectAll("circle");
graph.links.forEach(function(d) {
d.source = graph.nodes[d.source];
d.target = graph.nodes[d.target];
});
link = link.data(graph.links).enter().append("line")
.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
brush.call(d3.svg.brush()
.x(d3.scale.identity().domain([0, width]))
.y(d3.scale.identity().domain([0, height]))
.on("brushstart", function(d) {
node.each(function(d) { d.previouslySelected = shiftKey && d.selected; });
})
.on("brush", function() {
var extent = d3.event.target.extent();
node.classed("selected", function(d) {
return d.selected = d.previouslySelected ^
(extent[0][0] <= d.x && d.x < extent[1][0]
&& extent[0][1] <= d.y && d.y < extent[1][1]);
});
})
.on("brushend", function() {
d3.event.target.clear();
d3.select(this).call(d3.event.target);
}));
var force = d3.layout.force()
.charge(-120)
.linkDistance(30)
.nodes(graph.nodes)
.links(graph.links)
.size([width, height])
.start();
node = node.data(graph.nodes).enter().append("circle")
.attr("r", 4)
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
.on("mousedown", function(d) {
if (!d.selected) { // Don't deselect on shift-drag.
if (!shiftKey) node.classed("selected", function(p) { return p.selected = d === p; });
else d3.select(this).classed("selected", d.selected = true);
}
})
.on("mouseup", function(d) {
if (d.selected && shiftKey) d3.select(this).classed("selected", d.selected = false);
})
.call(d3.behavior.drag()
.on("dragstart", function(d1) {
node.filter(function(d) { return d.selected; })
.each(function(d) { d.fixed |= 2; })
})
.on("drag", function(d1) {
node.filter(function(d) { return d.selected; })
.each(function(d) {
d.x += d3.event.dx;
d.y += d3.event.dy;
d.px += d3.event.dx;
d.py += d3.event.dy;
})
force.resume();
})
.on("dragend", function(d) {
node.filter(function(d) { return d.selected; })
.each(function(d) { d.fixed &= ~6; })
}));
function tick() {
link.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
node.attr('cx', function(d) { return d.x; }).attr('cy', function(d) { return d.y; });
};
force.on("tick", tick);
function keydown() {
shiftKey = d3.event.shiftKey || d3.event.metaKey;
}
function keyup() {
shiftKey = d3.event.shiftKey || d3.event.metaKey;
}
.node {
stroke: #fff;
stroke-width: 1.5px;
}
.node .selected {
stroke: red;
}
.link {
stroke: #999;
}
.brush .extent {
fill-opacity: .1;
stroke: #fff;
shape-rendering: crispEdges;
}
<!DOCTYPE html>
<style>
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
</script>
This D3 block contains a v4 selectable Force-Layout Graph. It is based on the example you gave but using the syntax introduced in v4.
Hope it helps.
Related
d3 Node Labels and collapsing
I currently have a D3 that displays a force directed network and will collapse its children on click. I would like to to also display the names of the nodes on hover. Something like this: https://bl.ocks.org/mbostock/1212215 Below is my code so far. .node { cursor: pointer; stroke: #3182bd; stroke-width: 1.5px; } .link { fill: none; stroke: #9ecae1; stroke-width: 1.5px; } </style> <body> <script src="http://d3js.org/d3.v3.min.js"></script> <script> var width = 960, height = 500, root; var force = d3.layout.force() .size([width, height]) .on("tick", tick); var svg = d3.select("body").append("svg") .attr("width", width) .attr("height", height); //Added markers to indicate that this is a directed graph svg.append("defs").selectAll("marker") .data(["arrow"]) .enter().append("marker") .attr("id", function(d) { return d; }) .attr("viewBox", "0 -5 10 10") .attr("refX", 15) .attr("refY", -1.5) .attr("markerWidth", 4) .attr("markerHeight", 4) .attr("orient", "auto") .append("path") .attr("d", "M0,-5L10,0L0,5"); var link = svg.selectAll(".link"), node = svg.selectAll(".node"); d3.json("test.json", function(json) { root = json; //Give nodes ids and initialize variables for(var i=0; i<root.nodes.length; i++) { var node = root.nodes[i]; node.id = i; node.collapsing = 0; node.collapsed = false; } //Give links ids and initialize variables for(var i=0; i<root.links.length; i++) { var link = root.links[i]; link.source = root.nodes[link.source]; link.target = root.nodes[link.target]; link.id = i; } // for (var i=0; i<root.nodes.length; i++){ // var node = root.nodes[i]; // } update(); }); function update() { //Keep only the visible nodes var nodes = root.nodes.filter(function(d) { return d.collapsing == 0; }); var links = root.links; //Keep only the visible links links = root.links.filter(function(d) { return d.source.collapsing == 0 && d.target.collapsing == 0; }); force .nodes(nodes) .links(links) .start(); // Update the links… link = link.data(links, function(d) { return d.id; }); // Exit any old links. link.exit().remove(); // Enter any new links. link.enter().insert("line", ".node") .attr("class", "link") .attr("x1", function(d) { return d.source.x; }) .attr("y1", function(d) { return d.source.y; }) .attr("x2", function(d) { return d.target.x; }) .attr("y2", function(d) { return d.target.y; }) // Update the nodes… node = node.data(nodes, function(d){ return d.id; }).style("fill", color); // Exit any old nodes. node.exit().remove(); // Enter any new nodes. node.enter().append("circle") .attr("class", "node") .attr("cx", function(d) { return d.x; }) .attr("cy", function(d) { return d.y; }) .attr("r", function(d) { return Math.sqrt(d.size) / 10 || 4.5; }) .style("fill", color) .on("click", click) .call(force.drag); } function tick() { link.attr("x1", function(d) { return d.source.x; }) .attr("y1", function(d) { return d.source.y; }) .attr("x2", function(d) { return d.target.x; }) .attr("y2", function(d) { return d.target.y; }); node.attr("cx", function(d) { return d.x; }) .attr("cy", function(d) { return d.y; }); } // Color leaf nodes orange, and packages white or blue. function color(d) { return d.collapsed ? "#3182bd" : d.children ? "#c6dbef" : "#fd8d3c"; } // Toggle children on click. function click(d) { if (!d3.event.defaultPrevented) { //check if link is from this node, and if so, collapse root.links.forEach(function(l) { if(l.source.id == d.id) { if(d.collapsed){ l.target.collapsing--; } else { l.target.collapsing++; } } }); d.collapsed = !d.collapsed; } update(); } </script> My issue is with the update function. I was able to get it to display, but when I clicked to collapse, it wouldn't show the labels after. Im not sure if I should be tweeting the tick or what. thanks!
Group the circle and text labels using SVG group element as shown below. var groupNodes = node.enter().append("g") .attr("class", "node") .attr("transform", function(d) { return "translate("+d.x+","+d.y+")"; }); .on("click", click) .on("mouseover", function(){ d3.select(this).select("text").style("display","block"); }) .on("mouseout", function(){ d3.select(this).select("text").style("display","none"); }) .call(force.drag); groupNodes.append("circle") .attr("r", function(d) { return Math.sqrt(d.size) / 10 || 4.5; }) .style("fill", color); var label = groupNodes.append("text") .attr("dy", ".35em") .style("display", "none) .text(function(d) { return d.name; //Use the key which holds the name value }); The above code replaces following part of the code in the update function. node.enter().append("circle") .attr("class", "node") .attr("cx", function(d) { return d.x; }) .attr("cy", function(d) { return d.y; }) .attr("r", function(d) { return Math.sqrt(d.size) / 10 || 4.5; }) .style("fill", color) .on("click", click) .call(force.drag); and now change the tick function as follows function tick() { link.attr("x1", function(d) { return d.source.x; }) .attr("y1", function(d) { return d.source.y; }) .attr("x2", function(d) { return d.target.x; }) .attr("y2", function(d) { return d.target.y; }); node.attr("transform", function(d) { return "translate("+d.x+","+d.y+")"; }); } Now all other functionalities should work as expected.
Drag, drop and fix a node in D3JS
I have a HTML File to display graphs using D3JS. The JSFiddle is in this link here. I know that I must use the function to enable the fixing of node position on dragging and dropping: var drag = force.drag() .on("dragstart", dragstart); function dragstart(d) { d3.select(this).classed("fixed", d.fixed = true); } However, on dragging and dropping a node, I am unable to fix the position. I want the same functionality as shown in this link .I don't want the node to revert to its original position. How do I change it?
From this example : http://bl.ocks.org/mbostock/3750558 You need to add a call to drag function : var drag = force.drag() .on("dragstart", dragstart); Which is the following : function dragstart(d) { d3.select(this).classed("fixed", d.fixed = true); } The key point is setting d.fixed to true. EDIT In your fiddle you weren't calling node_drag. So you need to call that on the nodes like so : var node = gnodes.append("circle") .attr("class", "node") .attr("r", 5) .style("fill", function(d) { thelistofcolors[d.group] = color(d.group); return color(d.group); }) .call(node_drag); // <<<<<<<this line And then change the tick to a function so you can call it later : force.on("tick", tick); function tick() { link.attr("x1", function(d) { return d.source.x; }) .attr("y1", function(d) { return d.source.y; }) .attr("x2", function(d) { return d.target.x; }) .attr("y2", function(d) { return d.target.y; }); gnodes.attr("transform", function(d) { return 'translate(' + [d.x, d.y] + ')'; }); }; Updated fiddle with all data showing (not filtered on click) : https://jsfiddle.net/vgy1s8k3/4/
The solution is to set a 'fixed' node property to true on dragstart, this will allow to append the position. <!DOCTYPE html> <html> <head> <script type="text/javascript" src="//d3js.org/d3.v3.min.js"></script> <style> .node { stroke: #fff; stroke-width: 1.5px; } .link { stroke: #999; stroke-opacity: .6; } </style> </head> <body> <script> var width = 1280, height = 960; var color = d3.scale.category20(); var force = d3.layout.force() .charge(-120) .linkDistance(30) .size([width, height]); var svg = d3.select("body").append("svg") .attr("width", width) .attr("height", height); d3.json("graph.json", function(error, graph) { if (error) throw error; force .nodes(graph.nodes) .links(graph.links) .start(); var link = svg.selectAll("graph.link") .data(graph.links) .enter().append("line") .attr("class", "link") .style("stroke-width", function(d) { return Math.sqrt(d.value); }); var node = svg.selectAll("graph.node") .data(graph.nodes) .enter().append("circle") .attr("class", "node") .attr("r", 5) .style("fill", function(d) { return color(d.fill); }) .call(force.drag); var drag = force.drag() .on("dragstart", dragstart); function dragstart(d) { d.fixed = true; } node.append("title") .text(function(d) { return d.name }); force.on("tick", tick); function tick() { link.attr("x1", function(d) { return d.source.x; }) .attr("y1", function(d) { return d.source.y; }) .attr("x2", function(d) { return d.target.x; }) .attr("y2", function(d) { return d.target.y; }); node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; }); }; }); </script> </body> </html>
Displaying Node label in a dynamic forcelayout graph in D3.js
I'm trying to visualize a graph using D3.js via web sockets. I'm trying to display node label ( can be seen in the code below ), but it does not seem to appear at all. Please see the function start(). What is wrong here? <script> var width = 1900, height = 1080; var color = d3.scale.category10(); var nodes = [], links = []; var force = d3.layout.force() .nodes(nodes) .links(links) .charge(-100) .gravity(0.1) .linkDistance(100) .size([width, height]) .on("tick", tick); var svg = d3.select("body").append("svg").attr("width",width).attr("height", height); var node = svg.selectAll(".node"), link = svg.selectAll(".link"); function tick() { node.attr("cx", function(d) { return d.x; }) .attr("cy", function(d) { return d.y; }) link.attr("x1", function(d) { return d.source.x; }) .attr("y1", function(d) { return d.source.y; }) .attr("x2", function(d) { return d.target.x; }) .attr("y2", function(d) { return d.target.y; }); } // Add and remove elements on the graph object function addNode(id) { nodes.push({"name":id, "id":id}); start(); } function addEdge(edgeId,sourceId,targetId) { var sourceNode = findNode(sourceId); var targetNode = findNode(targetId); if((sourceNode !== undefined) && (targetNode !== undefined)) { links.push({"edgeId":edgeId, "source": sourceNode, "target": targetNode}); start(); } } function removeEdge(edgeId) { for (var i = 0; i < links.length; i++) { if (links[i].edgeId == edgeId) { links.splice(i, 1); break; } } start(); } var findNode = function (id) { for (var i=0; i < nodes.length; i++) { if (nodes[i].id === id) return nodes[i] }; } function start() { var drag = force.drag().origin(function(d) { return d; }).on("dragstart", dragstarted).on("drag", dragged).on("dragend", dragended); link = link.data(force.links(), function(d) { return d.source.id + "-" + d.target.id; }); link.enter().insert("line", ".node").attr("class", "link"); link.exit().remove(); node = node.data(force.nodes(), function(d) { return d.id;}); node.enter().append("circle").attr("class", "node") .attr("r", 5) .style("fill", function(d) { return color(d.group); }) .call(drag); node.append("text") .attr("dx", 12) .attr("dy", ".35em") .text(function(d) { return d.name }); node.exit().remove(); force.start(); } function dragstarted(d) { d3.event.sourceEvent.stopPropagation(); d3.select(this).classed("dragging", true); } function dragged(d) { d3.select(this).attr("cx", d.x = d3.event.x).attr("cy", d.y = d3.event.y); } function dragended(d) { d3.select(this).classed("dragging", false); } var socket = new WebSocket('ws://localhost:8887'); socket.onopen = function(){ console.log("Connection established, handle with function"); }; socket.onmessage = function(evt){ var obj = JSON.parse(evt.data); if(obj.operation == "nodeAdded") { addNode( obj.nodeId ); } if(obj.operation == "edgeAdded") { addEdge(obj.edgeId,obj.fromNodeId,obj.toNodeId); } if(obj.operation == "edgeRemoved") { removeEdge(obj.edgeId); } } </script>
You will have to group the circles and corresponding labels for each node. Try this way. node = node.data(force.nodes(), function(d) { return d.id;}) .enter().append("g") .attr("class", "node") .call(drag); node.append("circle") .attr("r", 5) .style("fill", function(d) { return color(d.group); }); node.append("text") .attr("dx", 12) .attr("dy", ".35em") .text(function(d) { return d.name }); Also update the tick function as shown below. function tick() { link.attr("x1", function(d) { return d.source.x; }) .attr("y1", function(d) { return d.source.y; }) .attr("x2", function(d) { return d.target.x; }) .attr("y2", function(d) { return d.target.y; }); node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; }); }
By default text elements are of the white color. Add .style("fill", "black"), so you can see them. node.append("text") .attr("dx", 12) .attr("dy", ".35em") .style("fill", "black") .text(function(d) { return d.name }); Also your node is the <circle> element. You can't append <text> element to the <circle> element ( it's not a container ). Use a <g> element and append <circle> and <text> to it or append <text> element to the <svg> element.
How to hide labels with d3?
I am making a timeline sort of project. As of right now, my code looks like the following: var width = 960, height = 500; var color = d3.scale.category20(); var force = d3.layout.force() .charge(-300) .linkDistance(30) .size([width, height]); var svg = d3.select("body").append("svg") .attr("width", width) .attr("height", height); d3.json("input.json", function(error, graph) { force .nodes(graph.nodes) .links(graph.links) .start(); var link = svg.selectAll(".link") .data(graph.links) .enter().append("line") .attr("class", "link") .style("stroke-width", function(d) { return Math.sqrt(d.value); }) .style("visibility", function(d) { return d.value == 2 ? "hidden" : "visible"; }); var node = svg.selectAll(".node") .data(graph.nodes) .enter().append("circle") .attr("class", "node") .attr("r", 20) .style("fill", function(d) { return color(d.group); }) .call(force.drag) .style("visibility", function(d) { return d.group == 1 ? "hidden" : "visible"; }) .on("mouseover", function(d) { if(d.group == 2) { node.filter(function(d) { return d.group == 1; }).style("visibility", "visible"); link.filter(function(d) { return d.value == 2; }).style("visibility", "visible"); texts.filter(function(d) { return d.value == 2; }).style("visibility", "visible"); } }).on("mouseout", function(d) { if(d.group == 2) { node.filter(function(d) { return d.group == 1; }).style("visibility", "hidden"); link.filter(function(d) { return d.value == 2; }).style("visibility", "hidden"); } }); var texts = svg.selectAll("text.label") .data(graph.nodes) .enter().append("text") .attr("class", "label") .attr("fill", "black") .text(function(d) { return d.name; }); force.on("tick", function() { link.attr("x1", function(d) { return d.source.x; }) .attr("y1", function(d) { return d.source.y; }) .attr("x2", function(d) { return d.target.x; }) .attr("y2", function(d) { return d.target.y; }); node.attr("cx", function(d) { return d.x; }) .attr("cy", function(d) { return d.y; }); texts.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; }); }); }); As of right now, the text, or labelsconstantly show, even when the nodes and links don't. I'm wondering what I need to do? I don't know if I can assign a value or group to the labels
D3.js - Changing force directed graph node from circle to svg:rect
I'm modifying this example of a collapsible, hierarchical force layout and I'm having trouble changing the nodes from circles to squares (using svg:rect). With my current code, all of the squares appear in the top left hand corner. Otherwise they're behaving relatively normally (I can click on them to expand/collapse the tree). Here is my CSS styles that I'm using for this and other functions: .node { cursor: pointer; } .node rect { fill: skyblue; stroke: green; stroke-width: 1.5px; } .node text { font: 10px sans-serif; } .link { fill: none; stroke: green; stroke-width: 1.5px; } #container div{ float:left; } .chartSpace div{ float:left; margin-right:screen.width-960px; } Here is the code for the force layout: function forceDirected(){ //Clear SVG d3.select("svg").remove(); var width = 960, height = 500, root; var force = d3.layout.force() .size([width, height]) .on("tick", tick); var svg = d3.select(".chartSpace").append("svg") .attr("width", width) .attr("height", height); var link = svg.selectAll(".link"), node = svg.selectAll(".node"); d3.json("flare.json", function(flare) { root = flare; update(); }); function update() { var nodes = flatten(root), links = d3.layout.tree().links(nodes); // Restart the force layout. force .nodes(nodes) .links(links) .start(); // Update the links… link = link.data(links, function(d) { return d.target.id; }); // Exit any old links. link.exit().remove(); // Enter any new links. link.enter().insert("line", ".node") .attr("class", "link") .attr("x1", function(d) { return d.source.x; }) .attr("y1", function(d) { return d.source.y; }) .attr("x2", function(d) { return d.target.x; }) .attr("y2", function(d) { return d.target.y; }); // Update the nodes… node = node.data(nodes, function(d) { return d.id; }).style("fill", color); // Exit any old nodes. node.exit().remove(); // Enter any new nodes. node.enter().append("svg:rect") .attr("class","node") .attr("cx", function(d) { return d.x; }) .attr("cy", function(d) { return d.y; }) .attr("width", "6") .attr("height", "6") .on("click", click) .call(force.drag); } function tick() { link.attr("x1", function(d) { return d.source.x; }) .attr("y1", function(d) { return d.source.y; }) .attr("x2", function(d) { return d.target.x; }) .attr("y2", function(d) { return d.target.y; }); node.attr("cx", function(d) { return d.x; }) .attr("cy", function(d) { return d.y; }); } // Stroke colors function strColor(d) { return d._children ? "green" : d.children ? "green" : "green"; } // Node colors function color(d) { return d._children ? "skyblue" : d.children ? "skyblue" : "skyblue"; } // Toggle children on click. function click(d) { if (!d3.event.defaultPrevented) { if (d.children) { d._children = d.children; d.children = null; } else { d.children = d._children; d._children = null; } update(); } } // Returns a list of all nodes under the root. function flatten(root) { var nodes = [], i = 0; function recurse(node) { if (node.children) node.children.forEach(recurse); if (!node.id) node.id = ++i; nodes.push(node); } recurse(root); return nodes; } }
node.enter().append("svg:rect") .attr("class","node") .attr("cx", function(d) { return d.x; }) .attr("cy", function(d) { return d.y; }) should be var rectSize = 6; node.enter().append("svg:rect") .attr("class","node") .attr("x", function(d) { return d.x + rectSize/2; }) .attr("y", function(d) { return d.y + rectSize/2; }) .attr("width", rectSize) .attr("height", rectSize) rect elements are positioned by setting the location of their upper left hand corner with x and y; circle elements' centers are set with cx and cy.