I am using collapsible tree as shown in this link. My data is very big so the view come out of this is collapsing with each other. screenshot is given below :-
Currently I am looking into this link . It is helping me to increase size of the tree dynamically. By this, I can change the merging of node as shown below :-
Now my question is how to increase the frame height dynamically?.
When I expand all the nodes then the tree size is more than canvas/frame height.
I have find a solution of this. I am changing the update function of Collapsible tree code. Below is my code:-
function update(source) {
var duration = d3.event && d3.event.altKey ? 5000 : 500;
// compute the new height
var levelWidth = [1];
var childCount = function(level, n) {
if(n.children && n.children.length > 0) {
if(levelWidth.length <= level + 1) levelWidth.push(0);
levelWidth[level+1] += n.children.length;
n.children.forEach(function(d) {`
childCount(level + 1, d);
});
}
};
childCount(0, root);
newHeight = d3.max(levelWidth) * 60; // 20 pixels per line
tree = tree.size([newHeight, width]);
d3.select("svg").remove();//TO REMOVE THE ALREADY SVG CONTENTS AND RELOAD ON EVERY UPDATE
svg = d3.select("body").append("svg");
svg.attr("width", width + margin.right + margin.left)
.attr("height", newHeight + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// 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; });
// 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")
.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", "-.75em")
.attr("text-anchor", function(d) { return d.children || d._children ? "end" : "start"; })
.text(function(d) { return d.name; })
.style("fill-opacity", 1e-2);
nodeEnter.append("text")
.attr("x", function(d) { return d.children || d._children ? -10 : 10; })
.attr("dy", "1.00em")
.attr("text-anchor", function(d) { return d.children || d._children ? "end" : "start"; })
.text(function(d) { return d.info1; })
.style("fill-opacity", 1e-2);
// 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", 6)
.style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });
nodeUpdate.selectAll("text")
.style("fill-opacity", 4);
// 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.selectAll("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;
});
}
Related
I am new to stackoverflow, forgive me if i made any mistake.
I have updated my D3 v5 tree code so that the root starts from top left. By referring to one of the stack overflow (D3 Tree orientation) i was able to achieve root starting from top left, but the children are not starting with position of Y-axis as there parent, it starts from 0th position of y-axis. Here is the issue i am facing please find the screenshot..
enter image description here
Please suggest solution to fix this issue.
Here is my code..
<script>
// Set the dimensions and margins of the diagram
var margin = {top: 20, right: 90, bottom: 30, left: 90},
width = 2000 - margin.left - margin.right,
height = 2500 - margin.top - margin.bottom;
svgid = "svgid";
// append the svg object to the body of the page
// appends a 'group' element to 'svg'
// moves the 'group' element to the top left margin
var svg = d3.select("body").append("svg")
.attr("width", width + margin.right + margin.left)
.attr("height", height + margin.top + margin.bottom)
.attr("id", svgid)
.append("g")
.attr("transform", "translate("
+ margin.left + "," + margin.top + ")");
var i = 0,
duration = 750,
root;
// declares a tree layout and assigns the size
var treemap = d3.tree().size([height, width]);
function draw_tree(start_at){
/*document.getElementById("svgid").scrollIntoView({block: "center"});*/
d3.json(start_at).then(function(data){
var treeData = data;
// Assigns parent, children, height, depth
root = d3.hierarchy(treeData, function(d) { return d.children; });
root.x0 = height / 2;
root.y0 = 0;
// Collapse after the second level
root.children.forEach(collapse);
update(root);
// Collapse the node and all it's children
function collapse(d) {
if(d.children) {
d._children = d.children
d._children.forEach(collapse)
d.children = null
}
}
function update(source) {
// Assigns the x and y position for the nodes
var treeData = treemap(root);
// Compute the new tree layout.
var nodes = treeData.descendants(),
links = treeData.descendants().slice(1);
// Normalize for fixed-depth.
/*nodes.forEach(function(d){ d.y = d.depth * 340});*/
var nodeMap = {};
nodes.forEach(function(d) {
if (!nodeMap[d.depth] || d.x < nodeMap[d.depth]){
nodeMap[d.depth] = d.x;
}
});
nodes.forEach(function(d) {
d.y = d.depth * 300;
d.x -= nodeMap[d.depth];
});
// ****************** Nodes section ***************************
// Update the nodes...
var node = svg.selectAll('g.node')
.data(nodes, function(d) {return d.id || (d.id = ++i); });
// Enter any new modes 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);
var rectHeight = 140, rectWidth = 240;
nodeEnter.append('rect')
.attr('class', 'node')
.attr("width", rectWidth)
.attr("height", rectHeight)
.attr("x", 0)
.attr("y", -4)
.attr("rx","5")
.on("mouseover", mouseover)
.on("mousemove", function(d){mousemove(d);})
.on("mouseout", mouseout)
.style("fill", function(d) {
return "#0091DA";
});
// Add labels for the nodes
nodeEnter.append('text')
.attr("dy", "-.35em")
.attr("x", function(d) {
return 13;
})
.attr("text-anchor", function(d) {
return "start";
})
.each(function (d) {
d3.select(this).append("tspan")
.attr("dy", "1.75em")
.html(function (d) {
return "Freq: "+d.data.frequency;
})
.attr("x", function(d) {
return 13;
})
d3.select(this).append("tspan")
.attr("dy", "1.75em")
.html(function (d) {
return "PD: "+d.data.page_domain;
})
.attr("x", function(d) {
return 13;
})
var lines = wordwrap(d.data.page_name, 35)
for (var i = 0; i < lines.length; i++) {
if(i == 0){
d3.select(this).append("tspan")
.attr("dy", "1.75em")
.text("PN: "+lines[i])
.attr("x", function(d) {
return 13;
})
}else{
d3.select(this).append("tspan")
.attr("dy", "1.75em")
.text(lines[i])
.attr("x", function(d) {
return 13;
})
}
}
d3.select(this).append("tspan")
.attr("dy", "1.75em")
.html(function (d) {
return "PL: <a href="+d.data.page_link+" target='_blank'>Link</a>";
})
.attr("x", function(d) {
return 13;
})
});
// UPDATE
var nodeUpdate = nodeEnter.merge(node);
// Transition to the proper position for the node
nodeUpdate.transition()
.duration(duration)
.attr("transform", function(d) {
return "translate(" + d.y + "," + d.x + ")";
});
// Update the node attributes and style
nodeUpdate.select('circle.node')
.attr('r', 10)
.style("fill", function(d) {
return d._children ? "lightsteelblue" : "#fff";
})
.attr('cursor', 'pointer');
// Remove any exiting nodes
var nodeExit = node.exit().transition()
.duration(duration)
.attr("transform", function(d) {
return "translate(" + source.y + "," + source.x + ")";
})
.remove();
// On exit reduce the node circles size to 0
nodeExit.select('circle')
.attr('r', 1e-6);
// On exit reduce the opacity of text labels
nodeExit.select('text')
.style('fill-opacity', 1e-6);
// ****************** links section ***************************
// Update the links...
var link = svg.selectAll('path.link')
.data(links, function(d) { return d.id; });
// Enter any new links at the parent's previous position.
var linkEnter = link.enter().insert('path', "g")
.attr("class", "link")
.attr('d', function(d){
var o = {x: source.x0, y: source.y0}
return diagonal(o, o)
});
// Transition links to their new position.
link.transition()
.duration(duration)
.attr("d", diagonal);
// UPDATE
var linkUpdate = linkEnter.merge(link);
// Transition back to the parent element position
linkUpdate.transition()
.duration(duration)
.attr('d', function(d){ return diagonal(d, d.parent) });
// Remove any exiting links
var linkExit = link.exit().transition()
.duration(duration)
.attr('d', function(d) {
var o = {x: source.x, y: source.y}
return diagonal(o, o)
})
.remove();
// Store the old positions for transition.
nodes.forEach(function(d){
d.x0 = d.x;
d.y0 = d.y;
});
// Creates a curved (diagonal) path from parent to the child nodes
function diagonal(s, d) {
path = `M ${s.y} ${s.x}
C ${(s.y + d.y) / 2} ${s.x},
${(s.y + d.y) / 2} ${d.x},
${d.y} ${d.x}`
return path
}
// 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;
}
if (d.parent) {
d.parent.children.forEach(function(element) {
if (d !== element) {
collapse(element);
}
});
}
update(d);
}
}
});
}
</script>
I have the following D3 js similar to
http://mbostock.github.io/d3/talk/20111018/tree.html
I want to have the tree structure auto update based on new json sent by the server.
The new json is sent every 5 seconds.
The issue is that I am not sure how to get rid of the old tree and just leave the updated tree. Currently it just draws a new tree image using d3 js under the most recent tree so very quickly there are tons of tree structures on the page. I would like to know how to remove the old tree image.
Any d3 or JavaScript input?
<style>
.node {
cursor: pointer;
}
.node circle {
fill: #fff;
stroke: steelblue;
stroke-width: 1.5px;
}
.node text {
font: 10px sans-serif;
}
.link {
fill: none;
stroke: #ccc;
stroke-width: 1.5px;
}
</style>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var treeData2 = <%- JSON.stringify(treeJSON) %>;
drawTree();
function drawTree () {
treeData2.name = "broadcaster";
console.log("tree data = ", treeData2);
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]; });
svg = d3.select("body").append("svg")
.style("overflow", "scroll")
.attr("width", width + margin.right + margin.left)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.append("svg:g")
.attr("class","drawarea")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
root = treeData2;
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);
var inter = setInterval(function() {
socket.get('/channel/13/tree', function(updatedJSON) {
console.log("Updated Json = ", updatedJSON);
treeData2 = updatedJSON;
})
drawTree();
}, 5000);
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; });
// 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"; });
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("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 = 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;
});
d3.select("svg")
.call(d3.behavior.zoom()
.scaleExtent([0.5, 5])
.on("zoom", zoom));
node
.exit()
.remove();
link
.exit()
.remove();
}
// 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);
}
// Allows for zoom and drag properties
function zoom() {
var scale = d3.event.scale,
translation = d3.event.translate,
tbound = -height * scale,
bbound = height * scale,
lbound = (-width + margin.right) * scale,
rbound = (width- margin.left) * scale;
// limit translation to thresholds
translation = [
Math.max(Math.min(translation[0], rbound), lbound),
Math.max(Math.min(translation[1], bbound), tbound)
];
d3.select(".drawarea")
.attr("transform", "translate(" + translation + ")" +
" scale(" + scale + ")");
}
}
</script>
To delete the old SVG, simply select and remove it:
d3.selectAll("svg").remove();
I am using D3.js to draw a tree, the tree are based on the json files.
E.G. There two json files: a.json and b.json. When I click a.json, draw the tree for a.json and draw the tree for b.json when clicked.
There are 4 nodes in a.json and 10 nodes in b.json file.
The question is when I click a.json first and then click b.json, the first 4 nodes (nodes of a.json) will remained(does not disappear), and the left 6 nodes of b.json will be showed.
If I click b.json first and click a.json later, the tree will only reduce 6 nodes, the node text will not change to the text of a.json.
$("body").on("click",".hash_name",function(){
var hs = $(this).attr('id');
var repo_path = $("#repo_path").val();
var file_name = $(this).attr('name');
$("#file_name").val(file_name);
var file_path = repo_path+'/'+file_name;
$.post('../../admini/feature_matrix/git_operation.php',
{
hs:hs,
repo_path:repo_path,
type:5
},
function (output){
$('#source_feature_content').html(JSON.stringify(output));
root = output;
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);
},"json");
});
var margin = {top: 10, right: 20, bottom: 20, left: 80},
width = 1024 - margin.right - margin.left,
height = 900 - margin.top - margin.bottom;
var i = 0,
duration = 300,
root;
var tree = d3.layout.tree()
.size([height, width]);
var diagonal = d3.svg.diagonal()
.projection(function(d) { return [d.y, d.x]; }); // change to vertical
var svg = d3.select("#feature_tree").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 + ")");
d3.select(self.frameElement).style("height", "800px");
function update(source) {
// Recompute the new height
var levelWidth = [1];
var childCount = function(level, n) {
if(n.children && n.children.length > 0) {
if(levelWidth.length <= level + 1) levelWidth.push(0);
levelWidth[level+1] += n.children.length;
n.children.forEach(function(d) {
childCount(level + 1, d);
});
}
};
childCount(0, root);
var newHeight = d3.max(levelWidth) * 50; // 50 pixels per line
tree = tree.size([newHeight, width]);
// 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;});
// 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"; });
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 + ")"; }); // change to vertical
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 + ")"; }) // change to vertical
.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();
nodes.forEach(function(d) {
d.x0 = d.x;
d.y0 = d.y;
});
}
function click(d) {
if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
}
update(d);
}
Hello i am very new t D3JS and I am writing a tree visualization. I am having difficulty on changing the layout of the tree from horizontal to vertical(top-to-bottom). I am able to successfully display the nodes vertically but the links are always horizontal. Any help would be appriciated. Thank you.
Here is my code:
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("#visualize")
.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);
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("rect")
.attr("y", function(d) { return d.children || d._children ? -5 : -5; })
.attr("width","9")
.attr("height","9")
.style("fill", function(d) { return d._children ? "skyblue" : "#fff"; })
.style("stroke","green")
.style("stroke-width", "1.5px");
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("rect")
.attr("y", function(d) { return d.children || d._children ? -5 : -5; })
.attr("width","9")
.attr("height","9")
.style("fill", function(d) { return d._children ? "skyblue" : "#fff"; })
.style("stroke","green");
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;
}
}
This is how i solved it #dev_marshell08
<script type="text/javascript">
var w = 1600,
h = 2600,
r = 6,
fill = d3.scale.category20();
var root;
var force = d3.layout.force()
.charge(-120)
.linkDistance(30)
.size([w, h]);
var svg = d3.select("#visualize").append("svg:svg")
.attr("width", w)
.attr("height", h);
d3.json("flare.json", function(json) {
root = json;
var nodes = flatten(root),
links = d3.layout.tree().links(nodes);
var link = svg.selectAll("line")
.data(links)
.enter().append("svg:line")
.attr("class", "link");
var node = svg.selectAll("circle")
.data(nodes)
.enter().append("svg:circle")
.attr("r", r - .75)
.style("fill", function(d) { return fill(d.group); })
.style("stroke", function(d) { return d3.rgb(fill(d.group)).darker(); })
.call(force.drag);
force
.nodes(nodes)
.links(links)
.on("tick", tick)
.start();
function tick(e) {
// Push sources up and targets down to form a weak tree.
var k = 6 * e.alpha;
links.forEach(function(d, i) {
d.source.y -= k;
d.target.y += k;
});
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; });
}
});
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;
}
</script>
I am using a d3.js tree, and would like to add nodes dynamically, rather than pre-loading the entire tree.
How would i modify the following in order to dynamically add additional JSON nodes when a node is clicked? (see link below, and code below)
http://mbostock.github.com/d3/talk/20111018/tree.html
So rather than pre-loading the entire tree, instead i would like to retrieve the child nodes only when the parent is clicked. I can retrieve the json for the child nodes in the toggle function, however i can't figure out how i can add them to the tree.
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) {
// I could retrieve the child nodes here, but how to add them to the tree?
if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
}
}
I was able to dynamically add nodes by adding the following code in the toggle function:
$.getJSON(addthese.json, function(addTheseJSON) {
var newnodes = tree.nodes(addTheseJSON.children).reverse();
d.children = newnodes[0];
update(d);
});
Notes: I am using jQuery to retrieve the json file
I'm new to D3, but this raw code might be helpful. You can create a JSON object and push it into the tree/link. Then it's just a matter of redrawing the tree.
function createTreeNode(source){
var current_node = tree.nodes(source);
var myJSONObject = {"name": "new Node","children": []};
if(current_node[0]._children!=null){
current_node[0]._children.push(myJSONObject);
console.log(current_node[0]._children);
source.children = source._children;
source._children = null;
}
else if(current_node[0].children!=null && current_node[0]._children!=null){
current_node[0].children.push(myJSONObject);
console.log(current_node[0].children);
}
else{
current_node[0].children=[]
current_node[0].children.push(myJSONObject);
console.log(current_node[0].children);
}
tree.links(current_node).push(current_node[current_node.length-1]);
navigate_tree(source);}