Skip to content

Instantly share code, notes, and snippets.

@mbostock
Last active February 9, 2016 01:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save mbostock/3087986 to your computer and use it in GitHub Desktop.
Save mbostock/3087986 to your computer and use it in GitHub Desktop.
Cross-linked Mouseover
license: gpl-3.0

This is a simple example of using CSS class names to support cross-linking between tree elements. Each element in the tree has an associated type field in the data, indicating whether the species is wild or domesticated. (This is bogus data, of course, and only for demonstration purposes.) When you mouseover one of the leaf nodes, other nodes of the same type will highlight.

The coordination happens in two places. First, the G elements for the nodes have a computed class attribute:

.attr("class", function(d) { return "node " + d.type; })

Next, register a mouseover and mouseout handler for interaction:

.on("mouseover", function(d) { highlight(d.type); })
.on("mouseout", function(d) { highlight(null); })

Finally, the highlight function selects nodes by class and toggles an active class which overrides the fill color.

function highlight(type) {
  if (type == null) d3.selectAll(".node").classed("active", false);
  else d3.selectAll(".node." + type).classed("active", true);
}

The active class is defined as:

.node.active {
  fill: red;
}

For more details on how to select related nodes, see my answer to how do you select (and then modify) related elements?

<!DOCTYPE html>
<meta charset="utf-8">
<style>
.link {
fill: none;
stroke: #aaa;
}
.node text {
font: 10px sans-serif;
}
.node circle {
stroke: #fff;
stroke-width: 1.5px;
}
.node.active {
fill: red;
}
</style>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script>
var margin = {top: 40, right: 40, bottom: 40, left: 80},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
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.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
d3.json("species.json", function(error, root) {
if (error) throw error;
var nodes = tree.nodes(root),
links = tree.links(nodes);
// Create the link lines.
svg.selectAll(".link")
.data(links)
.enter().append("path")
.attr("class", "link")
.attr("d", diagonal);
// Create the node circles.
var node = svg.selectAll(".node")
.data(nodes)
.enter().append("g")
.attr("class", function(d) { return "node " + d.type; })
.attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; })
.on("mouseover", function(d) { highlight(d.type); })
.on("mouseout", function(d) { highlight(null); });
node.append("circle")
.attr("r", 4.5);
node.append("text")
.attr("x", -6)
.attr("dy", ".35em")
.attr("text-anchor", "end")
.text(function(d) { return d.name; });
});
function highlight(type) {
if (type == null) d3.selectAll(".node").classed("active", false);
else d3.selectAll(".node." + type).classed("active", true);
}
</script>
{
"name": "Carnivora",
"children": [
{
"name": "Felidae",
"children": [
{
"name": "Felis",
"children": [
{
"name": "catus",
"type": "domesticated"
}
]
},
{
"name": "Panthera",
"children": [
{
"name": "tigris",
"type": "wild"
},
{
"name": "leo",
"type": "wild"
},
{
"name": "onca",
"type": "wild"
},
{
"name": "pardus",
"type": "wild"
}
]
}
]
},
{
"name": "Canidae",
"children": [
{
"name": "Canis",
"children": [
{
"name": "lupus",
"type": "domesticated"
},
{
"name": "latrans",
"type": "wild"
},
{
"name": "aureus",
"type": "wild"
}
]
}
]
}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment