A Pen by Sebastian Rothbucher on CodePen.
Created
April 7, 2024 15:20
-
-
Save sebastianrothbucher/00b2b6b32e1188cd10af9ef86174e739 to your computer and use it in GitHub Desktop.
d3-highlight-legend
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<h2>D3 with highlight via Legend</h2> | |
<div id="my_dataviz"></div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(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); | |
})); | |
})(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<script src="https://d3js.org/d3.v4.js"></script> | |
<script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
body { | |
font-family: sans-serif; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment