I'm using the d3.js library to paint some nodes and link them, but they don't link them correctly. I have tried too many things but none of them has given me a solution.
Like you can see in the image the path are linking only in the enter position of the svg:rect.
nodeEnter.append("svg:rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", 1)
.attr("height", 1)
.style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });
nodeEnter.append("svg:text")
.attr("x", 30)
.attr("dy", ".35em")
.attr("text-anchor", "")
.text(function(d) { return d.name + " " + (d.occurences || ""); })
.style("fill-opacity", 1e-6);
nodeEnter.append("svg:image")
.attr('width', 0)
.attr('height', 0)
.style("fill-opacity", 1e-6)
.attr("xlink:href", function(d) {return d.icon; });
// Transition nodes to their new position.
var nodeUpdate = node.transition()
.duration(duration)
.attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; });
nodeUpdate.select("rect")
.attr("rx", 6)
.attr("ry", 6)
.attr("x", 0)
.attr("y", -12.5)
.attr("width", function(d){ return (getWidthOfText(d.name, 'Helvetica Neue', 14)+getWidthOfText(d.ocurrences, 'Helvetica Neue', 14))+30;})
.attr("height", 25)
//.style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });
.style("filter", "url(#drop-shadow)");
nodeUpdate.select("text")
.style("fill-opacity", 1);
nodeUpdate.select("image")
.attr("x", 6)
.attr("y", -7)
.attr('width', 20)
.attr('height', 15)
.style("fill-opacity", 1);
// Transition exiting nodes to the parent's new position.
var nodeExit = node.exit().transition()
.duration(duration)
.attr("transform", function(d) { return "translate(" + source.y + "," + source.x + ")"; })
.remove();
nodeExit.select("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", 1)
.attr("height", 1)
.style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });
nodeExit.select("text")
.style("fill-opacity", 1e-6);
vis.selectAll("image")
.append("svg:title")
.text(function(d) { return "Descripción: "+d.description+"\n\nComentarios: "+d.comment});
// Update the links…
var link = vis.selectAll("path.link")
.data(tree.links(nodes), function(d) { return d.target.id; });
// Enter any new links at the parent's previous position.
link.enter().insert("svg:path", "g")
.attr("class", "link")
.attr("d", function(d) {
var o = {x: source.x0, y: source.y0};
return diagonal({source: o, target: o});
})
.transition()
.duration(duration)
.attr("d", diagonal);
// Transition links to their new position.
link.transition()
.duration(duration)
.attr("d", diagonal);
// Transition exiting nodes to the parent's new position.
link.exit().transition()
.duration(duration)
.attr("d", function(d) {
var o = {x: source.x, y: source.y};
return diagonal({source: o, target: o});
})
.remove();
// Stash the old positions for transition.
nodes.forEach(function(d) {
d.x0 = d.x;
d.y0 = d.y;
});
Is there any way to move the paths to get the nodes correctly linked?
Related
I am working on a project which needs a label for each of the levels of the collapsible tree in D3.js(https://bl.ocks.org/mbostock/4339083). I am facing tough time in adding the same. The label I require is added in the attached screenshot. The label should populate for each level as soon as click on each level and disappear as it the tree rolls back. Could anyone help me out with this.
Modify the update method to track the levels from the node items. Maintain a unique sorted hash of all the depth values from the node items used to plot the chart. Once you have the sorted depthHash array, plot the text on the top of your chart. Below is a fiddle i have modified for your reference.
http://jsfiddle.net/deepakpster/vomxqxov/3/
var margin = {top: 20, right: 120, bottom: 20, left: 120},
width = 960 - margin.right - margin.left,
height = 800 - margin.top - margin.bottom;
var i = 0,
duration = 750,
root;
var tree = d3.layout.tree()
.size([height, width]);
var diagonal = d3.svg.diagonal()
.projection(function(d) { return [d.y, d.x]; });
var svg = d3.select("body").append("svg")
.attr("width", width + margin.right + margin.left)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
flare = "http://rawgit.com/mbostock/1093025/raw/a05a94858375bd0ae023f6950a2b13fac5127637/flare.json"
d3.json(flare, function(error, flare) {
root = flare;
root.x0 = height / 2;
root.y0 = 0;
function collapse(d) {
if (d.children) {
d._children = d.children;
d._children.forEach(collapse);
d.children = null;
}
}
root.children.forEach(collapse);
update(root);
});
d3.select(self.frameElement).style("height", "800px");
function update(source) {
// Compute the new tree layout.
var nodes = tree.nodes(root).reverse(),
links = tree.links(nodes);
// Normalize for fixed-depth.
nodes.forEach(function(d) { d.y = d.depth * 180; });
// Showing the labels for the level of depths.
// Using underscore.js to do the pluck, uniq.
var depthHash = _.uniq(_.pluck(nodes, "depth")).sort();
svg.selectAll("g.levels-svg").remove();
var levelSVG = svg.append("g").attr("class", "levels-svg");
var levels = levelSVG.selectAll("g.level");
levels.data(depthHash)
.enter().append("g")
.attr("class", "level")
.attr("transform", function(d) { return "translate(" + d*180 + "," + 10 + ")"; })
.append("text")
.text(function(d){
return "level-" + d;
});
// Update the nodes…
var node = svg.selectAll("g.node")
.data(nodes, function(d) { return d.id || (d.id = ++i); });
// Enter any new nodes at the parent's previous position.
var nodeEnter = node.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) { return "translate(" + source.y0 + "," + source.x0 + ")"; })
.on("click", click);
/*
nodeEnter.append("circle")
.attr("r", 1e-6)
.style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });
*/
var ww = 30, hh = 20
nodeEnter.append("rect")
.attr("height", 1e-6)
.attr("width", 1e-6)
.attr("x", -ww/2)
.attr("y", -hh/2)
.attr("rx", 3)
.attr("ry", 3)
.style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });
nodeEnter.append("text")
.attr("x", function(d) { return d.children || d._children ? -10 : 10; })
.attr("dy", ".35em")
.attr("text-anchor", function(d) { return d.children || d._children ? "end" : "start"; })
.text(function(d) { return d.name; })
.style("fill-opacity", 1e-6);
// Transition nodes to their new position.
var nodeUpdate = node.transition()
.duration(duration)
.attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; });
nodeUpdate.select("rect")
.attr("width", ww)
.attr("height", hh)
.style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });
nodeUpdate.select("circle")
.attr("r", 4.5)
.style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });
nodeUpdate.select("text")
.attr("dx", -10)
.style("fill-opacity", 1);
// Transition exiting nodes to the parent's new position.
var nodeExit = node.exit().transition()
.duration(duration)
.attr("transform", function(d) { return "translate(" + source.y + "," + source.x + ")"; })
.remove();
nodeExit.select("circle")
.attr("r", 1e-6);
nodeExit.select("text")
.style("fill-opacity", 1e-6);
// Update the links…
var link = svg.selectAll("path.link")
.data(links, function(d) { return d.target.id; });
// Enter any new links at the parent's previous position.
link.enter().insert("path", "g")
.attr("class", "link")
.attr("d", function(d) {
var o = {x: source.x0, y: source.y0};
return diagonal({source: o, target: o});
});
// Transition links to their new position.
link.transition()
.duration(duration)
.attr("d", diagonal);
// Transition exiting nodes to the parent's new position.
link.exit().transition()
.duration(duration)
.attr("d", function(d) {
var o = {x: source.x, y: source.y};
return diagonal({source: o, target: o});
})
.remove();
// Stash the old positions for transition.
nodes.forEach(function(d) {
d.x0 = d.x;
d.y0 = d.y;
});
}
// Toggle children on click.
function click(d) {
if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
}
update(d);
}
var graph = Vue.component('graph', {
template: '#graph-template',
props: {
data:Array,
},
attached: function(){
this.genGraph();
},
methods:{
genGraph:function(){
this.data.forEach(function(obj) {
root = obj;
root.x0 = h / 2;
root.y0 = 0;
this.updateGraph(root);
})
},
updateGraph: function(source) {
var i = 0;
var duration = d3.event && d3.event.altKey ? 5000 : 500;
var nodes = this._tree.nodes(root).reverse();
nodes.forEach(function(d) { d.y = d.depth * 180; });
var node = this._svg.selectAll("g.node")
.data(nodes, function(d) {
return d.id || (d.id = ++i);
});
var nodeEnter = node.enter().append("svg:g")
.attr("class", "node")
.attr("transform", function(d) {
return "translate(" + source.y0 + "," + source.x0 + ")";
})
.on("click", function(d) {
this.updateGraph(d);
});
nodeEnter.append("svg:circle")
.attr("r", 0)
.style("fill", function( d ) {
return d._children ? "lightsteelblue" : "#fff";
});
nodeEnter.append('svg:foreignObject')
.attr("x", -3)
.attr("y", -15)
.append("xhtml:body")
.html(function(d){
return d.children || d._children ? "<i class='fa fa-server fa-2x' style='color:#e29e3d;'></i>" : "<i class='fa fa-server fa-2x' style='color:#03a9f4;'></i>";
});
nodeEnter.append("svg:text")
.attr("x", function(d) { return d.children || d._children ? - 15 : 25; })
.attr("dy", ".35em")
.attr("text-anchor", function(d) { return d.children || d._children ? "end" : "start"; })
.text(function(d) { return d.name; })
.style("fill-opacity", 1e-6);
nodeEnter.append("a")
.attr("xlink:href", function (d) {
return "/" + d.id;
})
.append("rect")
.attr("class", "clickable")
.attr("y", -10)
.attr("x", function (d) { return d.children || d._children ? -70 : 10; })
.attr("width", 60)
.attr("height", 16)
.style("fill","rgba(3,169,244,0)")
.style("fill-opacity", .3);
var nodeUpdate = node.transition()
.duration(duration)
.attr("transform", function( d ) {
return "translate(" + d.y + "," + d.x + ")";
});
nodeUpdate.select("circle")
.attr("r", 0)
.style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });
nodeUpdate.select("text")
.style("fill-opacity", 1)
.text(function(d){
return d.name.split("/").pop();
});
var nodeExit = node.exit().transition()
.duration(duration)
.attr("transform", function(d) { return "translate(" + source.y + "," + source.x + ")"; })
.remove();
nodeExit.select("circle")
.attr("r", 1e-6);
nodeExit.select("text")
.style("fill-opacity", 1e-6);
var link = this._svg.selectAll("path.link")
.data(this._tree.links(nodes), function(d) { return d.target.id; });
link.enter().insert("svg:path", "g")
.attr("class", function (d) { return (d.source != root) ? "link_dashed" : "link_continuous" ; })
.attr("d", function(d) {
var o = {x: source.x0, y: source.y0};
return diagonal({source: o, target: o});
})
.transition()
.duration(duration)
.attr("d", diagonal)
.attr("marker-end", "url(#end)");
link.transition()
.duration(duration)
.attr("d", diagonal)
.attr("marker-end", "url(#end)");
link.exit().transition()
.duration(duration)
.attr("d", function(d) {
var o = {x: source.x, y: source.y};
return diagonal({source: o, target: o});
})
.remove();
nodes.forEach(function(d) {
d.x0 = d.x;
d.y0 = d.y;
});
}
}
}
When the updateGraph() method is called, an error occurs:
Uncaught TypeError: this.updateGraph is not a function.
The reason you'll be getting this issue (based on your code above) is because this.updateGraph(root); is not in the same scope as your component.
Above the line this.data.forEach(function(obj) { add the following:
var self = this;
and then change:
this.updateGraph(root);
to:
self.updateGraph(root);
Hope this helps!
Hi so I'm trying to add multiple text elements to a d3 svg g node. However, for the following code, all the first text assignments to a node show up but none of the second text elements appear. Is my approach right? or is there a bug I'm missing
var node = svg.selectAll("g.node")
.data(nodes, function(d) { return d.id || (d.id = ++i); });
// Enter any new nodes at the parent's previous position.
var nodeEnter = node.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) { return "translate(" + source.y0 + "," + source.x0 + ")"; })
.on("click", click);
nodeEnter.append("circle")
.attr("r", 1e-6)
.style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });
// .on("mouseenter",setdatavis)
// .on("mouseleave",setdatainvis);
nodeEnter.append("text")
// .attr("x",0)
// .attr("y",0)
// .append("tspan")
.attr("x", function(d) { return d.children || d._children ? -10 : 10; })
.attr("y",-10)
.attr("dy", ".35em")
.attr("text-anchor", "end")
.text(function(d) { return d.name; })
.style("fill-opacity", 1e-6)
.style("visibility","visible");
// .on("mouseenter", function(){d3.select(this)
// .style("visibility", "hidden")
// .transition()
// ;})
// .on("mouseleave", function(){d3.select(this)
// .style("visibility", "visible")
// .transition()
// ;});
nodeEnter.append("text")
.attr("x",10)
.attr("text-anchor","start")
.attr("y",10)
.attr("dy",".35em")
.text(function(d){return "hi";})
.style("visibility","visible")
.style("fill-opacity",1e-6);
I'm trying to create a tree using D3 and am having trouble changing the text of my nodes after changing dataset. My code for updating/creating the tree is pasted below:
function update(source) {
var duration = d3.event && d3.event.altKey ? 5000 : 500;
// Compute the new tree layout.
var nodes = tree.nodes(root).reverse();
// Normalize for fixed-depth.
nodes.forEach(function(d) { d.y = d.depth * 60; });
// Update the nodes…
var node = vis.selectAll("g.node")
.data(nodes, function(d) { return d.id || (d.id = ++i); });
// Enter any new nodes at the parent's previous position.
var nodeEnter = node.enter().append("svg:g")
.attr("class", "node")
.attr("transform", function(d) { return "translate(" + source.y0 + "," + source.x0 + ")"; })
.on("click", function(d) { toggle(d); update(d); });
nodeEnter.append("svg:circle")
.attr("r", 1e-6)
.style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });
nodeEnter.append("svg:text")
.attr("x", function(d) { return d.children || d._children ? -10 : 10; })
.attr("dy", ".35em")
.attr("text-anchor", function(d) { return d.children || d._children ? "end" : "start"; })
.text(function(d) { console.log(d.name); return d.name; })
.style("fill-opacity", 1e-6);
// Transition nodes to their new position.
var nodeUpdate = node.transition()
.duration(duration)
.attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; });
nodeUpdate.select("circle")
.attr("r", function(d) {return d.size;} )//14.5)
.style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });
nodeUpdate.select("text")
.style("fill-opacity", 1);
// Transition exiting nodes to the parent's new position.
var nodeExit = node.exit().transition()
.duration(duration)
.attr("transform", function(d) { return "translate(" + source.y + "," + source.x + ")"; })
.remove();
nodeExit.select("circle")
.attr("r", 1e-6);
nodeExit.select("text")
.style("fill-opacity", 1e-6);
// Update the links…
var link = vis.selectAll("path.link")
.data(tree.links(nodes), function(d) { return d.target.id; });
// Enter any new links at the parent's previous position.
link.enter().insert("svg:path", "g")
.attr("class", "link")
.attr("d", function(d) {
var o = {x: source.x0, y: source.y0};
return diagonal({source: o, target: o});
})
.transition()
.duration(duration)
.attr("d", diagonal);
// Transition links to their new position.
link.transition()
.duration(duration)
.attr("d", diagonal);
// Transition exiting nodes to the parent's new position.
link.exit().transition()
.duration(duration)
.attr("d", function(d) {
var o = {x: source.x, y: source.y};
return diagonal({source: o, target: o});
})
.remove();
// Stash the old positions for transition.
nodes.forEach(function(d) {
d.x0 = d.x;
d.y0 = d.y;
});
}
On tree one you can see the first tree with each node having a text.
On tree two you can see how of the same text names, where as tree two has completely different node texts than the first first. It creates a mix of node texts from tree one and tree two.
My new data HAS all the new texts I want to use, but when I use console.log() to print them out it only prints out the few that it actually uses on tree two.
How do I make it update the text on all nodes?
You are not updating the text in the update selection. To do that, use something like the following code:
nodeUpdate.select("text")
.text(function(d) { console.log(d.name); return d.name; })
.style("fill-opacity", 1);
I'm new to the d3.js library.
I'm trying to make a tree like this one, but with a link that goes to an external page on each node.
Is it possible?
I tried to add a "svg:a" to each node but in makes all the tree to disappear.
Update:
I'm taking this code from the html of the page linked above.
The libraries linked are:
d3.js
d3.layout.js
This is all the code:
<style type="text/css">
.node circle {
cursor: pointer;
fill: #fff;
stroke: steelblue;
stroke-width: 1.5px;
}
.node text {
font-size: 11px;
}
path.link {
fill: none;
stroke: #ccc;
stroke-width: 1.5px;
}
</style>
<script>
var m = [20, 120, 20, 120],
w = 1280 - m[1] - m[3],
h = 800 - m[0] - m[2],
i = 0,
root;
var tree = d3.layout.tree()
.size([h, w]);
var diagonal = d3.svg.diagonal()
.projection(function(d) { return [d.y, d.x]; });
var vis = d3.select("#body").append("svg:svg")
.attr("width", w + m[1] + m[3])
.attr("height", h + m[0] + m[2])
.append("svg:g")
.attr("transform", "translate(" + m[3] + "," + m[0] + ")");
d3.json("flare.json", function(json) {
root = json;
root.x0 = h / 2;
root.y0 = 0;
function toggleAll(d) {
if (d.children) {
d.children.forEach(toggleAll);
toggle(d);
}
}
// Initialize the display to show a few nodes.
root.children.forEach(toggleAll);
toggle(root.children[1]);
toggle(root.children[1].children[2]);
toggle(root.children[9]);
toggle(root.children[9].children[0]);
update(root);
});
function update(source) {
var duration = d3.event && d3.event.altKey ? 5000 : 500;
// Compute the new tree layout.
var nodes = tree.nodes(root).reverse();
// Normalize for fixed-depth.
nodes.forEach(function(d) { d.y = d.depth * 180; });
// Update the nodes…
var node = vis.selectAll("g.node")
.data(nodes, function(d) { return d.id || (d.id = ++i); });
// Enter any new nodes at the parent's previous position.
var nodeEnter = node.enter().append("svg:g")
.attr("class", "node")
.attr("transform", function(d) { return "translate(" + source.y0 + "," + source.x0 + ")"; })
.on("click", function(d) { toggle(d); update(d); });
nodeEnter.append("svg:circle")
.attr("r", 1e-6)
.style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });
nodeEnter.append("svg:text")
.attr("x", function(d) { return d.children || d._children ? -10 : 10; })
.attr("dy", ".35em")
.attr("text-anchor", function(d) { return d.children || d._children ? "end" : "start"; })
.text(function(d) { return d.name; })
.style("fill-opacity", 1e-6);
// Transition nodes to their new position.
var nodeUpdate = node.transition()
.duration(duration)
.attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; });
nodeUpdate.select("circle")
.attr("r", 4.5)
.style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });
nodeUpdate.select("text")
.style("fill-opacity", 1);
// Transition exiting nodes to the parent's new position.
var nodeExit = node.exit().transition()
.duration(duration)
.attr("transform", function(d) { return "translate(" + source.y + "," + source.x + ")"; })
.remove();
nodeExit.select("circle")
.attr("r", 1e-6);
nodeExit.select("text")
.style("fill-opacity", 1e-6);
// Update the links…
var link = vis.selectAll("path.link")
.data(tree.links(nodes), function(d) { return d.target.id; });
// Enter any new links at the parent's previous position.
link.enter().insert("svg:path", "g")
.attr("class", "link")
.attr("d", function(d) {
var o = {x: source.x0, y: source.y0};
return diagonal({source: o, target: o});
})
.transition()
.duration(duration)
.attr("d", diagonal);
// Transition links to their new position.
link.transition()
.duration(duration)
.attr("d", diagonal);
// Transition exiting nodes to the parent's new position.
link.exit().transition()
.duration(duration)
.attr("d", function(d) {
var o = {x: source.x, y: source.y};
return diagonal({source: o, target: o});
})
.remove();
// Stash the old positions for transition.
nodes.forEach(function(d) {
d.x0 = d.x;
d.y0 = d.y;
});
}
// Toggle children.
function toggle(d) {
if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
}
}
</script>
Basically what I have tried was to add this piece of code just before the nodeEnter.append("svg:text")
nodeEnter.append("svg:a")
.attr("x", function(d) { return d.children || d._children ? -10 : 10; })
.attr("dy", ".35em")
.attr("text-anchor", function(d) { return d.children || d._children ? "end" : "start"; })
.text("http://example.com")
.style("fill-opacity", 1e-6);
Try adding the action on the node itself, like below, and change the cursor to pointer to give a hint to the user.
var node = vis.selectAll("g.node")
.data(nodes, function(d) { return d.id || (d.id = ++i); })
.style("cursor", "pointer")
.on("click", function() {
window.open("http://www.stackoverflow.com", '_blank').focus();
});
You should be able to add an HTML link to any of the objects that you want by adding an a tag as follows (this example is for the text that is associated with each node);
nodeEnter.append("a")
.attr("xlink:href", "http://example.com")
.append("svg:text")
.attr("x", function(d) { return d.children || d._children ? -10 : 10; })
.attr("dy", ".35em")
.attr("text-anchor", function(d) { return d.children || d._children ? "end" : "start"; })
.text(function(d) { return d.name; })
.style("fill-opacity", 1e-6);
If you had a seperate URL associated with each node (let's imagine that it's called link) it would just be a matter of retrieving the url with a function call similar to this;
nodeEnter.append("a")
.attr("xlink:href", function(d) { return d.link; })
.append("svg:text")
.attr("x", function(d) { return d.children || d._children ? -10 : 10; })
.attr("dy", ".35em")
.attr("text-anchor", function(d) { return d.children || d._children ? "end" : "start"; })
.text(function(d) { return d.name; })
.style("fill-opacity", 1e-6);
For a fuller description of adding a link see here: https://leanpub.com/D3-Tips-and-Tricks/read#leanpub-auto-adding-web-links-to-d3js-objects
For information on tree diagrams in general see here: https://leanpub.com/D3-Tips-and-Tricks/read#leanpub-auto-tree-diagrams