Replace current svg with new svg deleting older DOM elements as well - javascript

My D3 layout gets dynamically update based on button click. When i try and click on button to update the SVG, it always creates a new SVG below older svg.
As and when i click on button, it keeps on adding new SVG below older SVG.
What I want is it should replace current SVG and create updated chart at same place.
My code is below
// JQuery function for two buttons. Based on button click, I am selecting
input file name
$(document).ready(function (){
$("#link1").click(function(){
d3.select("#SVG_name").remove();
getData("readme-flare-imports.json");
});
$("#link2").click(function(){
d3.select("#SVG_name").remove();
getData("readme-flare-imports_1.json");
}
)});
var getData = function(fileName)
{
var diameter = 500,
radius = diameter / 2,
innerRadius = radius - 120;
var cluster = d3.layout.cluster()
.size([360, innerRadius])
.sort(null)
.value(function(d) { return d.size; });
var bundle = d3.layout.bundle();
var line = d3.svg.line.radial()
.interpolate("bundle")
.tension(.85)
.radius(function(d) { return d.y; })
.angle(function(d) { return d.x / 180 * Math.PI; });
var svg = d3.select("body").append("svg")
.attr("width", diameter)
.attr("height", diameter)
.append("g")
.attr("id","SVG_name")
.attr("transform", "translate(" + radius + "," + radius + ")");
var link = svg.append("g").selectAll(".link"),
node = svg.append("g").selectAll(".node");
var tip = d3.tip()
.attr('class', 'd3-tip')
.offset([-10, 0])
svg.call(tip);
d3.select(self.frameElement).style("height", diameter + "px");
d3.json(fileName, function(error, classes) {
var nodes = cluster.nodes(packageHierarchy(classes)),
links = packageImports(nodes);
link = link
.data(bundle(links))
.enter().append("path")
.each(function(d) { d.source = d[0], d.target = d[d.length - 1]; })
.attr("class", "link")
.attr("d", line);
node = node
.data(nodes.filter(function(n) { return !n.children; }))
.enter().append("text")
.attr("class", "node")
.attr("dx", function(d) { return d.x < 180 ? 8 : -8; })
.attr("dy", ".31em")
.attr("transform", function(d) { return "rotate(" + (d.x - 90) + ")translate(" + d.y + ")" + (d.x < 180 ? "" : "rotate(180)"); })
.style("text-anchor", function(d) { return d.x < 180 ? "start" : "end"; })
.text(function(d) { return d.key; })
.on("mouseover", mouseovered)
.on("mouseout", mouseouted);
});
function mouseovered(d) {
tip.html("<span style='color:red'>" + d.key + "</span>");
node
.each(function(n) { n.target = n.source = false; });
link
.classed("link--target", function(l) { if (l.target === d) return l.source.source = true; })
.classed("link--source", function(l) { if (l.source === d) return l.target.target = true; })
.filter(function(l) { return l.target === d || l.source === d; })
.each(function() { this.parentNode.appendChild(this); });
node
.classed("node--target", function(n) { return n.target; })
.classed("node--source", function(n) { return n.source; })
.classed("mouseover", tip.show);
}
function mouseouted(d) {
link
.classed("link--target", false)
.classed("link--source", false);
node
.classed("node--target", false)
.classed("node--source", false)
.classed("mouseout",tip.hide);
}
// Lazily construct the package hierarchy from class names.
function packageHierarchy(classes) {
var map = {};
function find(name, data) {
var node = map[name], i;
if (!node) {
node = map[name] = data || {name: name, children: []};
if (name.length) {
node.parent = find(name.substring(0, i = name.lastIndexOf(".")));
node.parent.children.push(node);
node.key = name.substring(i + 1);
}
}
return node;
}
classes.forEach(function(d) {
find(d.name, d);
});
return map[""];
}
// Return a list of imports for the given array of nodes.
function packageImports(nodes) {
var map = {},
imports = [];
// Compute a map from name to node.
nodes.forEach(function(d) {
map[d.name] = d;
});
// For each import, construct a link from the source to target node.
nodes.forEach(function(d) {
if (d.imports) d.imports.forEach(function(i) {
imports.push({source: map[d.name], target: map[i]});
});
});
return imports;
}
}
UPDATED
<body>
<div>
<button class="btn btn-default ok-button" id="link1"> Link1 </button>
<button class="btn btn-default ok-button" id="link2"> Link2 </button>
</div>
<script type="text/javascript" src="d3/d3.v3.min.js"></script>
<script src="http://labratrevenge.com/d3-tip/javascripts/d3.tip.v0.6.3.js"></script>
<script type="text/javascript" src="js/jquery-2.1.0.min.js"></script>
<script type="text/javascript" src="js/main.js"></script>
</body>

I'm not a d3 expert, but this code looks wrong to me:
var svg = d3.select("body").append("svg")
.attr("width", diameter)
.attr("height", diameter)
.append("g")
.attr("id","SVG_name")
To quote the d3 documentation for append():
Appends a new element with the specified name as the last child of
each element in the current selection, returning a new selection
containing the appended elements.
So in actual fact, you are assigning that id to the <g>, rather than the <svg>. So your
d3.select("#SVG_name").remove();
will be removing the <g>, not the <svg>.
Try this instead:
var svg = d3.select("body").append("svg")
.attr("width", diameter)
.attr("height", diameter)
.attr("id","SVG_name")
.append("g")

Related

How to add a SVG element as a html string in d3.js? [duplicate]

I have looked for answer to this but none of the similar questions help me in my situation. I have a D3 tree that creates new nodes at runtime. I would like to add HTML (so I can format) to a node when I mouseover that particular node. Right now I can add HTML but its unformatted. Please help!
JSFiddle: http://jsfiddle.net/Srx7z/
JS Code:
var width = 960,
height = 500;
var tree = d3.layout.tree()
.size([width - 20, height - 60]);
var root = {},
nodes = tree(root);
root.parent = root;
root.px = root.x;
root.py = root.y;
var diagonal = d3.svg.diagonal();
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(-30,40)");
var node = svg.selectAll(".node"),
link = svg.selectAll(".link");
var duration = 750;
$("#submit_button").click(function() {
update();
});
function update() {
if (nodes.length >= 500) return clearInterval(timer);
// Add a new node to a random parent.
var n = {id: nodes.length},
p = nodes[Math.random() * nodes.length | 0];
if (p.children) p.children.push(n); else p.children = [n];
nodes.push(n);
// Recompute the layout and data join.
node = node.data(tree.nodes(root), function (d) {
return d.id;
});
link = link.data(tree.links(nodes), function (d) {
return d.source.id + "-" + d.target.id;
});
// Add entering nodes in the parent’s old position.
var gelement = node.enter().append("g");
gelement.append("circle")
.attr("class", "node")
.attr("r", 20)
.attr("cx", function (d) {
return d.parent.px;
})
.attr("cy", function (d) {
return d.parent.py;
});
// Add entering links in the parent’s old position.
link.enter().insert("path", ".g.node")
.attr("class", "link")
.attr("d", function (d) {
var o = {x: d.source.px, y: d.source.py};
return diagonal({source: o, target: o});
})
.attr('pointer-events', 'none');
node.on("mouseover", function (d) {
var g = d3.select(this);
g.append("text").html('First Line <br> Second Line')
.classed('info', true)
.attr("x", function (d) {
return (d.x+20);
})
.attr("y", function (d) {
return (d.y);
})
.attr('pointer-events', 'none');
});
node.on("mouseout", function (d) {
d3.select(this).select('text.info').remove();
});
// Transition nodes and links to their new positions.
var t = svg.transition()
.duration(duration);
t.selectAll(".link")
.attr("d", diagonal);
t.selectAll(".node")
.attr("cx", function (d) {
return d.px = d.x;
})
.attr("cy", function (d) {
return d.py = d.y;
});
}
Using Lars Kotthoff's excellent direction, I got it working so I decided to post it for others and my own reference:
http://jsfiddle.net/FV4rL/2/
with the following code appended:
node.on("mouseover", function (d) {
var g = d3.select(this); // The node
var div = d3.select("body").append("div")
.attr('pointer-events', 'none')
.attr("class", "tooltip")
.style("opacity", 1)
.html("FIRST LINE <br> SECOND LINE")
.style("left", (d.x + 50 + "px"))
.style("top", (d.y +"px"));
});

D3.js Hierarchical Edge Bundling, how do i change the color of a text group?

So i took a code from d3.js and im playing with it, but i dont fully understand de json and js connection and i want to change the color of a certain text group, for example the flare.analytics group
i took the code from here, it includes the json code: https://gist.github.com/mbostock/7607999
Ill be showing the javascript down below.
thanks in advance.
var diameter = 960,
radius = diameter / 2,
innerRadius = radius - 120;
var cluster = d3.cluster()
.size([360, innerRadius]);
var line = d3.radialLine()
.curve(d3.curveBundle.beta(0.85))
.radius(function(d) { return d.y; })
.angle(function(d) { return d.x / 180 * Math.PI; });
var svg = d3.select("body").append("svg")
.attr("width", diameter)
.attr("height", diameter)
.append("g")
.attr("transform", "translate(" + radius + "," + radius + ")");
var link = svg.append("g").selectAll(".link"),
node = svg.append("g").selectAll(".node");
d3.json("flare.json", function(error, classes) {
if (error) throw error;
var root = packageHierarchy(classes)
.sum(function(d) { return d.size; });
cluster(root);
link = link
.data(packageImports(root.leaves()))
.enter().append("path")
.each(function(d) { d.source = d[0], d.target = d[d.length - 1]; })
.attr("class", "link")
.attr("d", line);
node = node
.data(root.leaves())
.enter().append("text")
.attr("class", "node")
.attr("dy", "0.31em")
.attr("transform", function(d) { return "rotate(" + (d.x - 90) + ")translate(" + (d.y + 8) + ",0)" + (d.x < 180 ? "" : "rotate(180)"); })
.attr("text-anchor", function(d) { return d.x < 180 ? "start" : "end"; })
.text(function(d) { return d.data.key; })
.on("mouseover", mouseovered)
.on("mouseout", mouseouted);
});
function mouseovered(d) {
node
.each(function(n) { n.target = n.source = false; });
link
.classed("link--target", function(l) { if (l.target === d) return l.source.source = true; })
.classed("link--source", function(l) { if (l.source === d) return l.target.target = true; })
.filter(function(l) { return l.target === d || l.source === d; })
.raise();
node
.classed("node--target", function(n) { return n.target; })
.classed("node--source", function(n) { return n.source; });
}
function mouseouted(d) {
link
.classed("link--target", false)
.classed("link--source", false);
node
.classed("node--target", false)
.classed("node--source", false);
}
// Lazily construct the package hierarchy from class names.
function packageHierarchy(classes) {
var map = {};
function find(name, data) {
var node = map[name], i;
if (!node) {
node = map[name] = data || {name: name, children: []};
if (name.length) {
node.parent = find(name.substring(0, i = name.lastIndexOf(".")));
node.parent.children.push(node);
node.key = name.substring(i + 1);
}
}
return node;
}
classes.forEach(function(d) {
find(d.name, d);
});
return d3.hierarchy(map[""]);
}
// Return a list of imports for the given array of nodes.
function packageImports(nodes) {
var map = {},
imports = [];
// Compute a map from name to node.
nodes.forEach(function(d) {
map[d.data.name] = d;
});
// For each import, construct a link from the source to target
node.
nodes.forEach(function(d) {
if (d.data.imports) d.data.imports.forEach(function(i) {
imports.push(map[d.data.name].path(map[i]));
});
});
return imports;
}
Graph
If you want to change the color of text from the data, You need pass a function to analyze the data for each node and return the specific color that you need.
In this case when the text node is appended:
node = node
.data(root.leaves())
.enter().append("text")
.attr("class", "node")
// SETING CONDICIONAL FILL FOR TEXT
.style("fill", function(d){
// if name of node has 'flare.analytics' return red, if not '#000000'
if (d.data.name.indexOf('flare.analytics') > -1) return '#ff0000';
return '#000000';
})
.attr("dy", "0.31em")
.attr("transform", function(d) { return "rotate(" + (d.x - 90) + ")translate(" + (d.y + 8) + ",0)" + (d.x < 180 ? "" : "rotate(180)"); })
.attr("text-anchor", function(d) { return d.x < 180 ? "start" : "end"; })
.text(function(d) { return d.data.key; })
.on("mouseover", mouseovered)
.on("mouseout", mouseouted);

D3(v5) collapsable tree with search

I am trying to display d3 collapsable tree with search, here is the reference example am using [Search tree][1]
[1]: https://bl.ocks.org/jjzieve/a743242f46321491a950 I am getting error select2() is not a function. I did not get any solution on v5, how to change the above example based on my code.
Here is my code
function searchTree(obj, search, path) {
if (obj.name === search) { //if search is found return, add the object to the path and return it
path.push(obj);
return path;
}
else if (obj.children || obj._children) { //if children are collapsed d3 object will have them instantiated as _children
var children = (obj.children) ? obj.children : obj._children;
for (var i = 0; i < children.length; i++) {
path.push(obj);// we assume this path is the right one
var found = searchTree(children[i], search, path);
if (found) {// we were right, this should return the bubbled-up path from the first if statement
return found;
}
else {//we were wrong, remove this parent from the path and continue iterating
path.pop();
}
}
}
else {//not the right object, return false so it will continue to iterate in the loop
return false;
}
}
function extract_select2_data(node, leaves, index) {
if (node.children) {
for (var i = 0; i < node.children.length; i++) {
index = extract_select2_data(node.children[i], leaves, index)[0];
}
}
else {
leaves.push({ id: ++index, text: node.name });
}
return [index, leaves];
}
console.log("left side hitting ");
// Set the dimensions and margins of the diagram
var margin = { top: 20, right: 120, bottom: 20, left: 120 },
width = 1280 * 10 - margin.right - margin.left,
height = 1200 *10 - 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(".col-md-6").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 + ")");
var i = 0,
duration = 750,
root, path,select2_data;
// declares a tree layout and assigns the size
var treemap = d3.tree().size([height, width]);
function openPaths(paths) {
for (var i = 0; i < paths.length; i++) {
if (paths[i].id !== "1") {//i.e. not root
paths[i].class = 'found';
if (paths[i]._children) { //if children are hidden: open them, otherwise: don't do anything
paths[i].children = paths[i]._children;
paths[i]._children = null;
}
update(paths[i]);
}
}
}
// Assigns parent, children, height, depth
root = d3.hierarchy(this.parentTreeObj, function (d) { return d.children });
select2_data = extract_select2_data(this.parentTreeObj,[],0)[1];
root.x0 = height / 2;
root.y0 = 0;
// Collapse after the second level
root.children.forEach(collapse);
update(root);
$(".col-md-6").select2({
data: select2_data,
containerCssClass: "search"
});
//attach search box listener
$(".col-md-6").on("select2-selecting", function (e) {
var paths = searchTree(root, e.object.text, []);
if (typeof (paths) !== "undefined") {
openPaths(paths);
}
else {
alert(e.object.text + " not found!");
}
})
d3.select(self.frameElement).style("height", "800px");
// 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) {
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);
var newHeight = d3.max(levelWidth) * 20;
treemap = d3.tree().size([newHeight, width]);
// 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 ? "#eb8c00" : "#da5506";
});
// 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 d.data.name;
});
// 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 || d._id ? "#eb8c00" : "#da5506";
})
.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)
});
// 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) {
console.log("left side hitting inside ");
console.log("in Left");
// console.log("idddd",d.data.id);
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;
// d._id = d.data.id;
// d.data.id = null;
} else {
d.children = d._children;
d._children = null;
// d.data.id = d._id;
// d._id = null;
}
update(d);
}
}
Can anyone please help me out this. Thanks in advance
Have you linked your file to the select2 library? For example, do you have this:
<link rel="stylesheet" type="text/css" href="http://cdnjs.cloudflare.com/ajax/libs/select2/3.5.2/select2.min.css"></link>
<script type="text/javascript" src="http://cdnjs.cloudflare.com/ajax/libs/select2/3.5.2/select2.min.js"></script>
If you could share a link through JSFiddle, bl.ocks, or in a repository, that would help out a lot too. Let me know if that helps.

Return value from JSON, not the children count

Instead of printing the number of children a specific node in flare.json has, I want to print the real value of the child node from flare.json. When hovering over a parent node (a node with children), I want the sum of all children (and sub-children, ...) values printed.
There are similar questions (not specific d3) suggesting a recursive function. But all the code snippets did not help me so far. Does d3 ship with some function to achieve this behavior?
var width = 300,
height = 300,
radius = Math.min(width, height) / 2,
color = d3.scale.category20c();
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height * .50 + ")");
var partition = d3.layout.partition()
.sort(null)
.size([2 * Math.PI, radius * radius])
.value(function(d) { return 1; });
var arc = d3.svg.arc()
.startAngle(function(d) { return d.x; })
.endAngle(function(d) { return d.x + d.dx; })
.innerRadius(function(d) { return Math.sqrt(d.y); })
.outerRadius(function(d) { return Math.sqrt(d.y + d.dy); });
d3.json("https://raw.githubusercontent.com/d3/d3-hierarchy/master/test/data/flare.json", function(error, root) {
if (error) throw error;
var data = root;
var path = svg.datum(root).selectAll("path")
.data(partition.nodes)
.enter()
.append("path")
.attr("display", function(d) { return d.depth ? null : "none"; }) // hide inner ring
.attr("d", arc)
.style("stroke", "#fff")
.style("fill-rule", "evenodd")
.style("fill", howToFill)
.each(stash)
.on("mouseover", onMouseOver)
.on("mouseout", onMouseOut);
var value = function(d) { return d.value; };
});
function stash(d) {
d.x0 = d.x;
d.dx0 = d.dx;
}
function arcTween(a) {
var i = d3.interpolate({x: a.x0, dx: a.dx0}, a);
return function(t) {
var b = i(t);
a.x0 = b.x;
a.dx0 = b.dx;
return arc(b);
};
}
function howToFill(d) {
if ( d.name == "-" ) {
return "#fff";
} else {
return color((d.children ? d : d.parent).name);
}
}
// Mouseover
function onMouseOver(d) {
d3.select(this)
svg.append("text")
.style("text-anchor", "middle")
.text(function() { return d.name + " - " + d.value; });
}
function onMouseOut(d) {
d3.select(this)
svg
.select("text")
.remove();
}
d3.select(self.frameElement).style("height", height + "px");
<!DOCTYPE html>
<meta charset="utf-8">
<head>
<link rel="stylesheet" type="text/css" href="main.css">
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
</head>
<body>
<script type="text/javascript" src="chart.js"></script>
</body>
Here is a version of your code which displays the actual counts:
var width = 300,
height = 300,
radius = Math.min(width, height) / 2,
color = d3.scale.category20c();
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height * .50 + ")");
var partition = d3.layout.partition()
.sort(null)
.size([2 * Math.PI, radius * radius])
.value(function(d) { return 1; });
var arc = d3.svg.arc()
.startAngle(function(d) { return d.x; })
.endAngle(function(d) { return d.x + d.dx; })
.innerRadius(function(d) { return Math.sqrt(d.y); })
.outerRadius(function(d) { return Math.sqrt(d.y + d.dy); });
d3.json("https://raw.githubusercontent.com/d3/d3-hierarchy/master/test/data/flare.json", function(error, root) {
if (error) throw error;
// Deep copy of the root object (for use by the mouseover function)
// since the d3 partitioning will modify root and only keep
// partitioning details:
var initialData = JSON.parse(JSON.stringify(root));
var data = root;
var path = svg.datum(root).selectAll("path")
.data(partition.nodes)
.enter()
.append("path")
.attr("display", function(d) { return d.depth ? null : "none"; }) // hide inner ring
.attr("d", arc)
.style("stroke", "#fff")
.style("fill-rule", "evenodd")
.style("fill", howToFill)
.each(stash)
.on("mouseover", onMouseOver)
.on("mouseout", onMouseOut);
var value = function(d) { return d.value; };
// Let's move the onMouseOver function inside the json extractor in order
// to easily work with "initialData":
function onMouseOver(d) {
d3.select(this)
svg.append("text")
.style("text-anchor", "middle")
//.text(function() { return d.name + " - " + d.value; });
.text(function() {
return d.name + " - " + sumValue(findElem(initialData, d.name, d.depth, 0));
});
}
});
function stash(d) {
d.x0 = d.x;
d.dx0 = d.dx;
}
function arcTween(a) {
var i = d3.interpolate({x: a.x0, dx: a.dx0}, a);
return function(t) {
var b = i(t);
a.x0 = b.x;
a.dx0 = b.dx;
return arc(b);
};
}
function howToFill(d) {
if ( d.name == "-" ) {
return "#fff";
} else {
return color((d.children ? d : d.parent).name);
}
}
function onMouseOut(d) {
d3.select(this)
svg
.select("text")
.remove();
}
d3.select(self.frameElement).style("height", height + "px");
function findElem(elmt, name, searchedDepth, currDepth) {
if (currDepth > searchedDepth)
return undefined
else if (elmt.name == name && searchedDepth == currDepth) {
return elmt;
}
else if (elmt.children) {
var result = elmt.children.map(c => findElem(c, name, searchedDepth, currDepth + 1)).filter(x => x);
return result.length == 1 ? result[0] : undefined;
}
else
return undefined;
}
function sumValue(elmt) {
try {
if (elmt.children)
return elmt.children.map(c => sumValue(c)).reduce((a, b) => a + b, 0);
else
return elmt.value;
} catch(err) {
// The try catch is there to hide the exception due to the "data"
// value which is not supported. Not sure how to cleanly fix this
// corner case:
return "error";
}
}
<meta charset="utf-8">
<head>
<link rel="stylesheet" type="text/css" href="main.css">
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
</head>
<body>
<script type="text/javascript" src="chart.js"></script>
</body>
During the mouseover, let's first find back the actual data element which is hovered. The thing is, the d3 layout partitioning removes data information from the model... So that's why we need to get back to the original data to find back the node in question:
function findElem(elmt, name, searchedDepth, currDepth) {
if (currDepth > searchedDepth)
return undefined
else if (elmt.name == name && searchedDepth == currDepth) {
return elmt;
}
else if (elmt.children) {
var result = elmt.children.map(c => findElem(c, name, searchedDepth, currDepth + 1)).filter(x => x);
return result.length == 1 ? result[0] : undefined;
}
else
return undefined;
}
Which is called from the mouseover function this way:
.text( function() {
return d.name + " - " + sumValue(findElem(initialData, d.name, d.depth, 0));
});
where initialData is a deep copy of the json (otherwise, since the extracted json root is modified by the partitioning, we would loose original data information).
Notice the usage of the depth in order to handle the fact that different nodes can have the same label (for instance there are several nodes at different depths named "data").
Once we have the node in question, we can recursively sum all children values this way:
function sumValue(elmt) {
if (elmt.children)
return elmt.children.map(c => sumValue(c)).reduce((a, b) => a + b, 0);
else
return elmt.value;
}

D3 Appending HTML to Nodes

I have looked for answer to this but none of the similar questions help me in my situation. I have a D3 tree that creates new nodes at runtime. I would like to add HTML (so I can format) to a node when I mouseover that particular node. Right now I can add HTML but its unformatted. Please help!
JSFiddle: http://jsfiddle.net/Srx7z/
JS Code:
var width = 960,
height = 500;
var tree = d3.layout.tree()
.size([width - 20, height - 60]);
var root = {},
nodes = tree(root);
root.parent = root;
root.px = root.x;
root.py = root.y;
var diagonal = d3.svg.diagonal();
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(-30,40)");
var node = svg.selectAll(".node"),
link = svg.selectAll(".link");
var duration = 750;
$("#submit_button").click(function() {
update();
});
function update() {
if (nodes.length >= 500) return clearInterval(timer);
// Add a new node to a random parent.
var n = {id: nodes.length},
p = nodes[Math.random() * nodes.length | 0];
if (p.children) p.children.push(n); else p.children = [n];
nodes.push(n);
// Recompute the layout and data join.
node = node.data(tree.nodes(root), function (d) {
return d.id;
});
link = link.data(tree.links(nodes), function (d) {
return d.source.id + "-" + d.target.id;
});
// Add entering nodes in the parent’s old position.
var gelement = node.enter().append("g");
gelement.append("circle")
.attr("class", "node")
.attr("r", 20)
.attr("cx", function (d) {
return d.parent.px;
})
.attr("cy", function (d) {
return d.parent.py;
});
// Add entering links in the parent’s old position.
link.enter().insert("path", ".g.node")
.attr("class", "link")
.attr("d", function (d) {
var o = {x: d.source.px, y: d.source.py};
return diagonal({source: o, target: o});
})
.attr('pointer-events', 'none');
node.on("mouseover", function (d) {
var g = d3.select(this);
g.append("text").html('First Line <br> Second Line')
.classed('info', true)
.attr("x", function (d) {
return (d.x+20);
})
.attr("y", function (d) {
return (d.y);
})
.attr('pointer-events', 'none');
});
node.on("mouseout", function (d) {
d3.select(this).select('text.info').remove();
});
// Transition nodes and links to their new positions.
var t = svg.transition()
.duration(duration);
t.selectAll(".link")
.attr("d", diagonal);
t.selectAll(".node")
.attr("cx", function (d) {
return d.px = d.x;
})
.attr("cy", function (d) {
return d.py = d.y;
});
}
Using Lars Kotthoff's excellent direction, I got it working so I decided to post it for others and my own reference:
http://jsfiddle.net/FV4rL/2/
with the following code appended:
node.on("mouseover", function (d) {
var g = d3.select(this); // The node
var div = d3.select("body").append("div")
.attr('pointer-events', 'none')
.attr("class", "tooltip")
.style("opacity", 1)
.html("FIRST LINE <br> SECOND LINE")
.style("left", (d.x + 50 + "px"))
.style("top", (d.y +"px"));
});

Categories

Resources