Would like to add an infobox to a d3.js
file.
Have studied this SO question, and this SO question, and this SO question, and this fiddle.
Most of the following HTML
+ JavaScrip
+ d3.js
+ JSON
file works as planned.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script src="http://labratrevenge.com/d3-tip/javascripts/d3.tip.v0.6.3.js" charset="utf-8"></script>
<style type="text/css">
.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; }
.link:hover { stroke:blue; }
div#tooltip{ color:#ffffff; background:#000000; opacity:1; padding:5px; }
</style>
<title>Soils with Local JSON</title>
</head>
<body>
<h1>Soils with Local JSON</h1>
<div id="tooltip" style="display:none"></div>
<script type="text/javascript">
var margin = {
top: 20,
right: 120,
bottom: 20,
left: 120
},
width = 1000 - margin.right - margin.left,
height = 400 - 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];
});
//Add tool tip: d3-tip.
var tip = d3.tip()
.attr('class', 'd3-tip')
.html(function(d) { return '<a href="' + d.url + '">' + d.name + '</a>' });
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 + ")")
//Call tool-tip in var svg =
.call(tip);
var root = {
"name":"Soil","children":[
{"name":"Albaqualfs","url":"http://en.wikipedia.org/wiki/Albaqualfs","children":[
{"name":"Aeric Albaqualfs","children":[
{"name":"Auxvasse"},
{"name":"Cayagua"},
{"name":"Mamou"},
{"name":"Marine"},
{"name":"Medoc"},
{"name":"Springfield"},
{"name":"Tenot"}]}]}]};
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;
});
// 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)
.on("mouseover", function (d) {
var r = d3.select(this).node().getBoundingClientRect();
d3.select("div#tooltip")
.style("display", "inline")
.style("top", (r.top-25) + "px")
.style("left", r.left + "px")
.style("position", "absolute")
.text(d.test);
})
.on("mouseout", function(){
d3.select("div#tooltip").style("display", "none")
});
// 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;
});
}
// 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);
}
</script>
</body>
</html>
Now would like to add an infobox for an item that displays information like this:
Am currently working with d3-tip
.
The included file has a JSON field for "name" and "url". It also includes an attempt to map the "url" to d.url
and "name" to d.name
.
One issue is the need to keep the infobox temporarily persistent so that users can click the item's URL
if they choose. Once the cursor is moved away from the infobox, it should clear.
The target infobox image above shows an X
to close the infobox if clicked. However, I don't see how to introduce a second selector - the current selector opens the next level of the tree. Therefore, I'm hoping that a temporarily persistent infobox, revealed on mouseover, can be implemented.
The infobox can overlay the tree data - no need to re-draw the tree to adjust to the placement of the infobox.
How to correct the d3-tip code to work as desired?
If an SO reader uses an alternative to d3-tip
that basically accomplishes the same task, that would be good too.