Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save sebastianrothbucher/00b2b6b32e1188cd10af9ef86174e739 to your computer and use it in GitHub Desktop.
Save sebastianrothbucher/00b2b6b32e1188cd10af9ef86174e739 to your computer and use it in GitHub Desktop.
d3-highlight-legend
<h2>D3 with highlight via Legend</h2>
<div id="my_dataviz"></div>
(async () => {
const data = [ // days and visits by different groups
['Mon', 10, 12, 8],
['Tue', 20, 14, 7],
['Wed', 12, 8, 16],
];
// starting pt: https://d3-graph-gallery.com/graph/barplot_stacked_highlight.html
const groups = data.map(r => r[0]);
const subgroups = Array.from(new Set(data.map(r => r.slice(1)).map((_, i) => i + 1).flat())).sort().map(c => 'C' + c); // label for each of the 3 numbers
//console.log(subgroups);
const dataMapped = data.map(r => {
const rObj = {day: r[0]};
r.slice(1).forEach((n, i) => rObj['C' + (i + 1)] = n);
return rObj;
});
//console.log(dataMapped);
const margin = {top: 10, right: 30, bottom: 20, left: 50},
width = 460 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
const legendWidth = 120;
const legendPos = width + margin.left + margin.right;
const svg = d3.select("#my_dataviz")
.append("svg")
.attr("width", width + margin.left + margin.right + legendWidth)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
// now build a stack chart per se
const x = d3.scaleBand()
.domain(groups)
.range([0, width])
.padding([0.2])
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x).tickSizeOuter(0));
const y = d3.scaleLinear()
.domain([0, data.map(r => r.slice(1).reduce((p, c) => p + c, 0)).reduce((p, c) => Math.max(p, c), 0)])
.range([ height, 0 ]);
svg.append("g")
.call(d3.axisLeft(y));
const color = d3.scaleOrdinal()
.domain(subgroups)
.range(d3.schemeSet3); // https://observablehq.com/@d3/color-schemes
const stackedData = d3.stack()
.keys(subgroups)
(dataMapped);
//console.log(stackedData);
svg.append("g")
.selectAll("g")
// Enter in the stack data = loop key per key = group per group
.data(stackedData)
.enter().append("g")
.attr("fill", function(d) { return color(d.key); })
.style("opacity", 0.8)
.attr("class", function(d){ return d.key }) // Add a class to each subgroup: their name
.selectAll("rect")
// enter a second time = loop subgroup per subgroup to add all rectangles
.data(function(d) { return d; })
.enter().append("rect")
.attr("x", function(d) { return x(d.data.day); })
.attr("y", function(d) { return y(d[1]); })
.attr("height", function(d) { return y(d[0]) - y(d[1]); })
.attr("width",x.bandwidth())
.attr("stroke", "grey")
// legend - again with help: https://d3-graph-gallery.com/graph/custom_legend.html
const labelRow = svg.append("g")
.selectAll("g")
.data([...subgroups].reverse())
.enter()
.append("g")
.attr("transform", function(d,i){ return "translate(" + (legendPos - 20) + "," + (100 + i * 25) + ")"}) // 100 is where the first dot appears. 25 is the dots
.style("fill", function(d){ return color(d)})
.attr("class", function(d){ return d }) // Add a class to each subgroup: their name;
labelRow.append('text')
.text(function(d){ return d})
.attr("text-anchor", "left")
.style("alignment-baseline", "middle")
.style("cursor", "default");
labelRow.append("circle")
.attr("cx", -10)
.attr("cy", -2)
.attr("r", 7)
.style("fill", function(d){ return color(d)});
labelRow.on("mouseover", (d) => subgroups.forEach(sg => {
svg.selectAll("." + sg)
.style("opacity", d === sg ? 1 : 0.2);
}));
labelRow.on("mouseleave", () => subgroups.forEach(sg => {
svg.selectAll("." + sg)
.style("opacity", 0.8);
}));
})();
<script src="https://d3js.org/d3.v4.js"></script>
<script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script>
body {
font-family: sans-serif;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment