I'm following this example here: https://bl.ocks.org/d3noob/43a860bc0024792f8803bba8ca0d5ecd
On this part, I have:
// Enter any new links at the parent's previous position.
var linkEnter = link.enter().insert('path', "g")
.attr("class", "link")
.text(function(d) {
console.log(d.data.expression);
return d.data.expression;
})
.attr('d', function(d){
var o = {x: source.x0, y: source.y0}
return diagonal(o, o)
});
I see the output to the console, but I don't see the text visibly on the link.
Below is the whole update() method.
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 * 180});
// ****************** 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);
// Add Circle for the nodes
nodeEnter.append('circle')
.attr('class', 'node')
.attr('r', 1e-6)
.style("fill", function(d) {
return d._children ? "lightsteelblue" : "#fff";
});
// Add labels for the nodes
nodeEnter.append('text')
.attr("dy", ".35em")
.attr("x", function(d) {
return d.children || d._children ? -13 : 13;
})
.attr("text-anchor", function(d) {
return d.children || d._children ? "end" : "start";
})
.text(function(d) { return "expression: " + d.data.expression; });
// 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")
.text(function(d) {
console.log(d.data.expression);
return d.data.expression;
})
.attr('d', function(d){
var o = {x: source.x0, y: source.y0}
return diagonal(o, o)
});
// 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;
}
update(d);
}
}
If anyone can shed some light, I appreciate it. Thank you!
It's not very clear what you want to achieve with this line, but here is the problem:
After the insert...
var linkEnter = link.enter().insert('path', "g")
... this selection is a <path> selection. And, of course, there is no point in setting the text attribute of a path (which has none).
Whatever you want, break the block in two selections:
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)
});
var fooEnter = link.enter().insert('text', "g")
.attr("class", "link")
.text(function(d) {
console.log(d.data.expression);
return d.data.expression;
});
Related
I want to change the tree within the link bellow to v5 of d3js. Note that the current tree is in v3. I'm not familiar with d3js v5 :( I know that there are a lot of experts here.
I have changed the script version in this link codepen.io/augbog/pen/LEXZKK from v3 to v5. Namely, to be like this ,but it doesn't read the property 'tree' in this command "d3.layout.tree().nodeSize([70, 40]);"
This is the code that I'm using
https://codepen.io/augbog/pen/LEXZKK
var i = 0,
duration = 750,
rectW = 60,
rectH = 30;
var tree = d3.layout.tree().nodeSize([70, 40]);
var diagonal = d3.svg.diagonal()
.projection(function (d) {
return [d.x + rectW / 2, d.y + rectH / 2];
});
var svg = d3.select("#body").append("svg").attr("width", 1000).attr("height", 1000)
.call(zm = d3.behavior.zoom().scaleExtent([1,3]).on("zoom", redraw)).append("g")
.attr("transform", "translate(" + 350 + "," + 20 + ")");
//necessary so that zoom knows where to zoom and unzoom from
zm.translate([350, 20]);
root.x0 = 0;
root.y0 = height / 2;
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("#body").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.x0 + "," + source.y0 + ")";
})
.on("click", click);
nodeEnter.append("rect")
.attr("width", rectW)
.attr("height", rectH)
.attr("stroke", "black")
.attr("stroke-width", 1)
.style("fill", function (d) {
return d._children ? "lightsteelblue" : "#fff";
});
nodeEnter.append("text")
.attr("x", rectW / 2)
.attr("y", rectH / 2)
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.text(function (d) {
return d.name;
});
// Transition nodes to their new position.
var nodeUpdate = node.transition()
.duration(duration)
.attr("transform", function (d) {
return "translate(" + d.x + "," + d.y + ")";
});
nodeUpdate.select("rect")
.attr("width", rectW)
.attr("height", rectH)
.attr("stroke", "black")
.attr("stroke-width", 1)
.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.x + "," + source.y + ")";
})
.remove();
nodeExit.select("rect")
.attr("width", rectW)
.attr("height", rectH)
//.attr("width", bbox.getBBox().width)""
//.attr("height", bbox.getBBox().height)
.attr("stroke", "black")
.attr("stroke-width", 1);
nodeExit.select("text");
// 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("x", rectW / 2)
.attr("y", rectH / 2)
.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);
}
//Redraw for zoom
function redraw() {
//console.log("here", d3.event.translate, d3.event.scale);
svg.attr("transform",
"translate(" + d3.event.translate + ")"
+ " scale(" + d3.event.scale + ")");
}
There are several changes in d3v5 and your current version (d3v3). A lot of them are not backward compatible.
d3.layout.tree() is invalid since d3v4. Entire d3.layout.layoutName has been done away with. In d3v5, the correct way is to use d3.tree().nodeSize([w, h]).
For more information, you should see the official changelog for both v4 and v5. Also check and compare the API references for tree in d3v5 and d3v3.
This is my treeMap right now:
I want to append a button between the nodes. The nodes have parents and child relationships. The 2 white circle with plus sign are children of the coursera picture. So I am trying to append the button by calling a function that takes the parent's (x,y) and the children(x,y), then create a circle there probably.
Here is my full code, I did not write most of this code so I am not understanding things fully. I recognize that the link path between nodes were drawn by this function: ` function diagonal(s, d) {
var 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;
}`
How would like append a circle between 2 nodes in my code?
Full code: `
import React, { Component } from "react";
import * as d3 from "d3";
import { hierarchy, tree } from "d3-hierarchy";
import "../Tree.css";
//import "./Tree.css";
class Tree extends Component {
constructor(props) {
super(props);
this.state = { collapse: false, text: "hi", visible: true };
//this.toggle = this.toggle.bind(this);
}
handleChange = d => {
this.props.on_click_change(d);
};
componentDidMount() {
var that = this;
var treeData = this.props.roadmapData;
// Set the dimensions and margins of the diagramS
var height1 = window.innerHeight;
var margin = { top: 0, right: 0, bottom: 0, left: 0 },
width = 1080 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
// 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("li")
.append("svg")
.call(
d3.zoom().on("zoom", function() {
svg.attr("transform", d3.event.transform);
})
)
.attr("width", 1800 - margin.right - margin.left)
.attr("height", 900 - margin.top - margin.bottom)
.append("g")
.attr("transform", "translate(" + +"," + margin.top + ")");
var i = 0,
duration = 500,
root;
// declares a tree layout and assigns the size
var treemap = d3.tree().size([window.innerHeight, window.innerWidth]);
root = d3.hierarchy(treeData, function(d) {
return d.children;
});
root.x0 = height / 2;
root.y0 = 0;
console.log(this.props.treeData);
// 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),
more_button = treeData.descendants();
// Normalize for fixed-depth.
nodes.forEach(function(d) {
d.y = d.depth * 180;
});
// ****************** 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")
//if deleted, bubbles come from the very top, is weird
.attr("transform", function(d) {
return "translate(" + source.y0 + "," + source.x0 + ")";
});
// Add Circle for the nodes
nodeEnter
.append("circle")
.attr("class", "node")
.attr("r", 1e-6)
.style("fill", function(d) {
return d._children ? "lightsteelblue" : "#fff";
});
/*
// Add labels for the nodes
nodeEnter
.append("text")
.attr("dy", 0)
.attr("x", function(d) {
return d.children || d._children ? -13 : 13;
})
.attr("text-anchor", function(d) {
return d.children || d._children ? "end" : "start";
})
.text(function(d) {
return d.data.name;
});
*/
var diameter = 30;
nodeEnter
.append("image")
.on("click", click)
.attr("xlink:href", function(d) {
return d.data.website_image;
})
.attr("height", diameter * 2)
.attr("transform", "translate(-30," + -30 + ")");
// 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", diameter)
.style("fill", function(d) {
return d._children ? "lightsteelblue" : "#fff";
})
.attr("cursor", "pointer");
nodeUpdate
.append("circle")
.on("click", click2)
.attr("additional", "extra_circle")
.attr("r", 10)
.attr("transform", "translate(0," + -40 + ")");
// 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")
.style("fill", "red")
.attr("d", function(d) {
var o = { x: source.x0, y: source.y0 };
return diagonal(o, o);
});
// UPDATE
var linkUpdate = linkEnter.merge(link);
// Transition back to the parent element position
linkUpdate
.transition()
.duration(duration)
.attr("d", function(d) {
console.log(d, d.parent);
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) {
var 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;
}
update(d);
}
function click2(d) {
console.log(d.data.name);
that.setState({ text: d.data.details });
that.handleChange(d);
}
}
}
render() {
return null;
}
}
export default Tree;
`
Thanks to Coola, here's the solution:
`nodeEnter
.append("circle")
.attr("class", "extra_info")
.on("click", function(d) {})
.attr("cy", function(d) {
if (d.parent != null) {
d.x_pos = d.x;
d.parent_x_pos = d.parent.x;
}
if (d.parent_x_pos != null) {
return (d.x_pos + d.parent_x_pos) / 2 - d.x_pos;
}
})
.attr("cx", -90)
.attr("r", 7);`
So basically whenever you do function(d){} instead of just calling a single thing, the d variable gets passed and it contains a lot of information, including the parent of the current element and the child if there is one, and the x y positions, etc. d refers to the current element we are at. So we can comfortably use d.parent.x and d.x to calculate positions, here's what it looks like now:
Btw, d3 and svg's x and y seems to be reversed sometimes. As you can see: I am deciding cy, SVG circle's attribute on determining its y position by d.x.
I am making a d3 v4 vertical tree diagram based on http://bl.ocks.org/d3noob/8326869, and I'm trying to add text labels to the paths (eg. Yes, No, etc.). This is the code in the links section that I have so far:
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 * 95
});
// ****************** 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.x0 + "," + source.y0 + ")";
})
.attr("data-info", function (d) { return d.data.info })
.on('click', click);
// Add Circle for the nodes
/*nodeEnter.append('circle')
.attr('class', 'node')
.attr("id", function(d){return "node" + d.id;})//id of the node.
.attr('r', 1e-6)
.style("fill", function (d) {
return d._children ? "lightsteelblue" : "#fff";
});*/
nodeEnter.append('rect')
.attr('class', 'node')
.attr('width', 170)
.attr('height', 55)
.attr('x', -85)
.attr('y', -22)
.attr('rx',7) //rounding
.attr('ry',7)
.attr("id", function(d){return "node" + d.id;});//id of the node.
nodeEnter.append('text')
//.attr("dy", ".25em")
//.attr('x', -23)
.attr('class', 'node-text')
.attr("text-anchor", "middle")
.text(function (d) {
return d.data.name;
})
.call(wrap, 165);
// 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.x + "," + d.y + ")";
});
// Update the node attributes and style
nodeUpdate.select('rect.node')
.style("fill", function (d) {
if (d._children) {
return "#007bff"; // dark blue
}
})
.attr('cursor', 'pointer');
// Remove any exiting nodes
var nodeExit = node.exit().transition()
.duration(duration)
.attr("transform", function (d) {
return "translate(" + source.x + "," + source.y + ")";
})
.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("id", function(d){ return ("link" + d.id)})//unique id
.attr("class", "link")
.attr('d', function (d) {
var o = {x: source.x0, y: source.y0}
return diagonal(o, o);
});
// UPDATE
var linkUpdate = linkEnter.merge(link);
var linkLabel = link.enter().insert("text","g")
.attr("class", "link2")
.attr("id", function(d){ return ("link-label" + d.id)})//unique id
.attr("dx",function(d){ return (d.parent.x + d.x)/2 })
.attr("dy",function(d){ return (d.parent.y + d.y)/2 })
.text(function(d) {
if (d.data.label === "Yes") {
this.setAttribute("x",-30);
} else {
this.setAttribute("x",10);
}
return d.data.label;
});
linkUpdate.merge(linkLabel);
// Transition back to the parent element position
linkUpdate.transition()
.duration(duration)
.attr('d', function (d) {
svg.select("#link-label" + d.id).transition().duration(duration).attr("dx",function(d){ return (d.parent.x + d.x)/2 })
.attr("dy",function(d){ return (d.parent.y + d.y)/2});
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};
svg.selectAll("#link-label" + d.id).remove();
return diagonal(o, o)
})
.remove();
// Store the old positions for transition.
nodes.forEach(function (d) {
d.x0 = d.x;
d.y0 = d.y;
});
The "linkEnter.insert("text","g")" part is where I am having trouble. It looks like it keeps appending to the element instead of being a sibling so that it doesn't display at all, and I'm not sure how to code it so that it isn't a child of , while still being animated appropriately when a node is hidden/shown.
I'm also not sure how to set up the coordinates once I do manage to fix the above issue so that the text label is in the middle of the path (or at least close to it).
I've tried adding similar code in the node section instead but then it seems harder to find the right positioning to align to the path.
========
Edit: I've updated the code as it seems like it's working now. I created linkLabel which pulls data from the JSON variable. It's positioned to be next to the paths but not overlapping them. The animations aren't smooth but at least it seems to work.
i want to use d3.js to make data show in a tree. This is what I have done ,there is a problem that the svg will be so crowd when there are many data exist
Can you help me make it look better.
Here is my code
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) {
if (d._children) {
return "lightsteelblue";
}
if (d.isHealth != undefined) {
return d.isHealth == false ? "red" : "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 = 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;
});
}
I met the same problem. And I solved it by using nodeSize.
tree.nodeSize([x, y])
It will set separation between nodes. But this method will override the tree.size()
so you need to recount the coordinate about another nodes.
I hope my answer will help you.
I have tried this this, and it works but it replaces my whole tree because it is at the zero index of the nodes.
var newguys= jQuery.parseJSON(getLevel(2,'123456'));
var newnodes = tree.nodes(newguys).reverse();
d.children = newnodes[0];
update(d);
So I tried push,
// Toggle children on click.
function click(d) {
var newguys= jQuery.parseJSON(getLevel(2,'123456'));
if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
}
nodes.children.push(newguys);
update(d);
}
Update is the standard update function of d3 (Let us assume I have the json already read). I cannot get the new data on the end of the parent element (or anywhere else.)
var margin = {top: 20, right: 120, bottom: 20, left: 120},
width = 960 - margin.right - margin.left,
height = 1800 - 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 + ")");
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;
});
}
I do not know if this is the correct way, but here is what I did to finally insert a new node, code modified from all my travels. I can't find this anywhere, so I hope it helps someone:
// Toggle children.
function toggle(d) {
//works.
var a;
if(d.children)
{
a={"name": "No More Records"};
d.children.push(a);
} else {
d3.xhr(url)
.header("X-Requested-With", "XMLHttpRequest")
.header("Content-Type", "application/x-www-form-urlencoded")
.post("level=1&emp="+d.empid, function (error, request) {
if (error) return console.warn(error.responseText);
var folks = jQuery.parseJSON(request.responseText);
d.children = [folks];
update_new(d);
});
}
}
I have update_new because I did some things to make the added child nodes look different, but updating is essentially the same as the stock update(d) function.