Clickable legend added to Mike Bostock's stacked bar chart to select display of an individual category. With help from yuuniverse4444’s block #8325617.
Last active
February 19, 2018 19:28
-
-
Save KatiRG/5f168b5c884b1f9c36a5 to your computer and use it in GitHub Desktop.
d3js clickable stacked bar chart
This file contains hidden or 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
<!--helped by: http://bl.ocks.org/yuuniverse4444/8325617--> | |
<!DOCTYPE html> | |
<meta charset="utf-8"> | |
<style> | |
body { | |
font: 10px sans-serif; | |
} | |
.axis path, | |
.axis line { | |
fill: none; | |
stroke: #000; | |
shape-rendering: crispEdges; | |
} | |
.bar { | |
fill: steelblue; | |
} | |
.x.axis path { | |
display: none; | |
} | |
.tooltip{ | |
text-anchor: middle; | |
font-family: sans-serif; | |
font-size: 10px; | |
font-weight: bold; | |
fill:black; | |
} | |
</style> | |
<body> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.13/d3.min.js"></script> | |
<script> | |
var margin = {top: 20, right: 20, bottom: 30, left: 40}, | |
width = 960 - margin.left - margin.right, | |
height = 500 - margin.top - margin.bottom; | |
var x = d3.scale.ordinal() | |
.rangeRoundBands([0, width], .1); | |
var y = d3.scale.linear() | |
.rangeRound([height, 0]); | |
var color = d3.scale.ordinal() | |
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]); | |
var xAxis = d3.svg.axis() | |
.scale(x) | |
.orient("bottom"); | |
var yAxis = d3.svg.axis() | |
.scale(y) | |
.orient("left") | |
.tickFormat(d3.format(".2s")); | |
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 + ")"); | |
var active_link = "0"; //to control legend selections and hover | |
var legendClicked; //to control legend selections | |
var legendClassArray = []; //store legend classes to select bars in plotSingle() | |
var y_orig; //to store original y-posn | |
d3.csv("state_data.csv", function(error, data) { | |
if (error) throw error; | |
color.domain(d3.keys(data[0]).filter(function(key) { return key !== "State"; })); | |
data.forEach(function(d) { | |
var mystate = d.State; //add to stock code | |
var y0 = 0; | |
//d.ages = color.domain().map(function(name) { return {name: name, y0: y0, y1: y0 += +d[name]}; }); | |
d.ages = color.domain().map(function(name) { return {mystate:mystate, name: name, y0: y0, y1: y0 += +d[name]}; }); | |
d.total = d.ages[d.ages.length - 1].y1; | |
}); | |
data.sort(function(a, b) { return b.total - a.total; }); | |
x.domain(data.map(function(d) { return d.State; })); | |
y.domain([0, d3.max(data, function(d) { return d.total; })]); | |
svg.append("g") | |
.attr("class", "x axis") | |
.attr("transform", "translate(0," + height + ")") | |
.call(xAxis); | |
svg.append("g") | |
.attr("class", "y axis") | |
.call(yAxis) | |
.append("text") | |
.attr("transform", "rotate(-90)") | |
.attr("y", 6) | |
.attr("dy", ".71em") | |
.style("text-anchor", "end"); | |
//.text("Population"); | |
var state = svg.selectAll(".state") | |
.data(data) | |
.enter().append("g") | |
.attr("class", "g") | |
.attr("transform", function(d) { return "translate(" + "0" + ",0)"; }); | |
//.attr("transform", function(d) { return "translate(" + x(d.State) + ",0)"; }) | |
state.selectAll("rect") | |
.data(function(d) { | |
return d.ages; | |
}) | |
.enter().append("rect") | |
.attr("width", x.rangeBand()) | |
.attr("y", function(d) { return y(d.y1); }) | |
.attr("x",function(d) { //add to stock code | |
return x(d.mystate) | |
}) | |
.attr("height", function(d) { return y(d.y0) - y(d.y1); }) | |
.attr("class", function(d) { | |
classLabel = d.name.replace(/\s/g, ''); //remove spaces | |
return "class" + classLabel; | |
}) | |
.style("fill", function(d) { return color(d.name); }); | |
state.selectAll("rect") | |
.on("mouseover", function(d){ | |
var delta = d.y1 - d.y0; | |
var xPos = parseFloat(d3.select(this).attr("x")); | |
var yPos = parseFloat(d3.select(this).attr("y")); | |
var height = parseFloat(d3.select(this).attr("height")) | |
d3.select(this).attr("stroke","blue").attr("stroke-width",0.8); | |
svg.append("text") | |
.attr("x",xPos) | |
.attr("y",yPos +height/2) | |
.attr("class","tooltip") | |
.text(d.name +": "+ delta); | |
}) | |
.on("mouseout",function(){ | |
svg.select(".tooltip").remove(); | |
d3.select(this).attr("stroke","pink").attr("stroke-width",0.2); | |
}) | |
var legend = svg.selectAll(".legend") | |
.data(color.domain().slice().reverse()) | |
.enter().append("g") | |
//.attr("class", "legend") | |
.attr("class", function (d) { | |
legendClassArray.push(d.replace(/\s/g, '')); //remove spaces | |
return "legend"; | |
}) | |
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; }); | |
//reverse order to match order in which bars are stacked | |
legendClassArray = legendClassArray.reverse(); | |
legend.append("rect") | |
.attr("x", width - 18) | |
.attr("width", 18) | |
.attr("height", 18) | |
.style("fill", color) | |
.attr("id", function (d, i) { | |
return "id" + d.replace(/\s/g, ''); | |
}) | |
.on("mouseover",function(){ | |
if (active_link === "0") d3.select(this).style("cursor", "pointer"); | |
else { | |
if (active_link.split("class").pop() === this.id.split("id").pop()) { | |
d3.select(this).style("cursor", "pointer"); | |
} else d3.select(this).style("cursor", "auto"); | |
} | |
}) | |
.on("click",function(d){ | |
if (active_link === "0") { //nothing selected, turn on this selection | |
d3.select(this) | |
.style("stroke", "black") | |
.style("stroke-width", 2); | |
active_link = this.id.split("id").pop(); | |
plotSingle(this); | |
//gray out the others | |
for (i = 0; i < legendClassArray.length; i++) { | |
if (legendClassArray[i] != active_link) { | |
d3.select("#id" + legendClassArray[i]) | |
.style("opacity", 0.5); | |
} | |
} | |
} else { //deactivate | |
if (active_link === this.id.split("id").pop()) {//active square selected; turn it OFF | |
d3.select(this) | |
.style("stroke", "none"); | |
active_link = "0"; //reset | |
//restore remaining boxes to normal opacity | |
for (i = 0; i < legendClassArray.length; i++) { | |
d3.select("#id" + legendClassArray[i]) | |
.style("opacity", 1); | |
} | |
//restore plot to original | |
restorePlot(d); | |
} | |
} //end active_link check | |
}); | |
legend.append("text") | |
.attr("x", width - 24) | |
.attr("y", 9) | |
.attr("dy", ".35em") | |
.style("text-anchor", "end") | |
.text(function(d) { return d; }); | |
function restorePlot(d) { | |
state.selectAll("rect").forEach(function (d, i) { | |
//restore shifted bars to original posn | |
d3.select(d[idx]) | |
.transition() | |
.duration(1000) | |
.attr("y", y_orig[i]); | |
}) | |
//restore opacity of erased bars | |
for (i = 0; i < legendClassArray.length; i++) { | |
if (legendClassArray[i] != class_keep) { | |
d3.selectAll(".class" + legendClassArray[i]) | |
.transition() | |
.duration(1000) | |
.delay(750) | |
.style("opacity", 1); | |
} | |
} | |
} | |
function plotSingle(d) { | |
class_keep = d.id.split("id").pop(); | |
idx = legendClassArray.indexOf(class_keep); | |
//erase all but selected bars by setting opacity to 0 | |
for (i = 0; i < legendClassArray.length; i++) { | |
if (legendClassArray[i] != class_keep) { | |
d3.selectAll(".class" + legendClassArray[i]) | |
.transition() | |
.duration(1000) | |
.style("opacity", 0); | |
} | |
} | |
//lower the bars to start on x-axis | |
y_orig = []; | |
state.selectAll("rect").forEach(function (d, i) { | |
//get height and y posn of base bar and selected bar | |
h_keep = d3.select(d[idx]).attr("height"); | |
y_keep = d3.select(d[idx]).attr("y"); | |
//store y_base in array to restore plot | |
y_orig.push(y_keep); | |
h_base = d3.select(d[0]).attr("height"); | |
y_base = d3.select(d[0]).attr("y"); | |
h_shift = h_keep - h_base; | |
y_new = y_base - h_shift; | |
//reposition selected bars | |
d3.select(d[idx]) | |
.transition() | |
.ease("bounce") | |
.duration(1000) | |
.delay(750) | |
.attr("y", y_new); | |
}) | |
} | |
}); | |
</script> |
This file contains hidden or 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
State | Under 5 Years | 5 to 13 Years | 14 to 17 Years | 18 to 24 Years | 25 to 44 Years | 45 to 64 Years | 65 Years and Over | |
---|---|---|---|---|---|---|---|---|
AL | 310504 | 552339 | 259034 | 450818 | 1231572 | 1215966 | 641667 | |
AK | 52083 | 85640 | 42153 | 74257 | 198724 | 183159 | 50277 | |
AZ | 515910 | 828669 | 362642 | 601943 | 1804762 | 1523681 | 862573 | |
AR | 202070 | 343207 | 157204 | 264160 | 754420 | 727124 | 407205 | |
CA | 2704659 | 4499890 | 2159981 | 3853788 | 10604510 | 8819342 | 4114496 | |
CO | 358280 | 587154 | 261701 | 466194 | 1464939 | 1290094 | 511094 | |
CT | 211637 | 403658 | 196918 | 325110 | 916955 | 968967 | 478007 | |
DE | 59319 | 99496 | 47414 | 84464 | 230183 | 230528 | 121688 | |
DC | 36352 | 50439 | 25225 | 75569 | 193557 | 140043 | 70648 | |
FL | 1140516 | 1938695 | 925060 | 1607297 | 4782119 | 4746856 | 3187797 | |
GA | 740521 | 1250460 | 557860 | 919876 | 2846985 | 2389018 | 981024 | |
HI | 87207 | 134025 | 64011 | 124834 | 356237 | 331817 | 190067 | |
ID | 121746 | 201192 | 89702 | 147606 | 406247 | 375173 | 182150 | |
IL | 894368 | 1558919 | 725973 | 1311479 | 3596343 | 3239173 | 1575308 | |
IN | 443089 | 780199 | 361393 | 605863 | 1724528 | 1647881 | 813839 | |
IA | 201321 | 345409 | 165883 | 306398 | 750505 | 788485 | 444554 | |
KS | 202529 | 342134 | 155822 | 293114 | 728166 | 713663 | 366706 | |
KY | 284601 | 493536 | 229927 | 381394 | 1179637 | 1134283 | 565867 | |
LA | 310716 | 542341 | 254916 | 471275 | 1162463 | 1128771 | 540314 | |
ME | 71459 | 133656 | 69752 | 112682 | 331809 | 397911 | 199187 | |
MD | 371787 | 651923 | 316873 | 543470 | 1556225 | 1513754 | 679565 | |
MA | 383568 | 701752 | 341713 | 665879 | 1782449 | 1751508 | 871098 | |
MI | 625526 | 1179503 | 585169 | 974480 | 2628322 | 2706100 | 1304322 | |
MN | 358471 | 606802 | 289371 | 507289 | 1416063 | 1391878 | 650519 | |
MS | 220813 | 371502 | 174405 | 305964 | 764203 | 730133 | 371598 | |
MO | 399450 | 690476 | 331543 | 560463 | 1569626 | 1554812 | 805235 | |
MT | 61114 | 106088 | 53156 | 95232 | 236297 | 278241 | 137312 | |
NE | 132092 | 215265 | 99638 | 186657 | 457177 | 451756 | 240847 | |
NV | 199175 | 325650 | 142976 | 212379 | 769913 | 653357 | 296717 | |
NH | 75297 | 144235 | 73826 | 119114 | 345109 | 388250 | 169978 | |
NJ | 557421 | 1011656 | 478505 | 769321 | 2379649 | 2335168 | 1150941 | |
NM | 148323 | 241326 | 112801 | 203097 | 517154 | 501604 | 260051 | |
NY | 1208495 | 2141490 | 1058031 | 1999120 | 5355235 | 5120254 | 2607672 | |
NC | 652823 | 1097890 | 492964 | 883397 | 2575603 | 2380685 | 1139052 | |
ND | 41896 | 67358 | 33794 | 82629 | 154913 | 166615 | 94276 | |
OH | 743750 | 1340492 | 646135 | 1081734 | 3019147 | 3083815 | 1570837 | |
OK | 266547 | 438926 | 200562 | 369916 | 957085 | 918688 | 490637 | |
OR | 243483 | 424167 | 199925 | 338162 | 1044056 | 1036269 | 503998 | |
PA | 737462 | 1345341 | 679201 | 1203944 | 3157759 | 3414001 | 1910571 | |
RI | 60934 | 111408 | 56198 | 114502 | 277779 | 282321 | 147646 | |
SC | 303024 | 517803 | 245400 | 438147 | 1193112 | 1186019 | 596295 | |
SD | 58566 | 94438 | 45305 | 82869 | 196738 | 210178 | 116100 | |
TN | 416334 | 725948 | 336312 | 550612 | 1719433 | 1646623 | 819626 | |
TX | 2027307 | 3277946 | 1420518 | 2454721 | 7017731 | 5656528 | 2472223 | |
UT | 268916 | 413034 | 167685 | 329585 | 772024 | 538978 | 246202 | |
VT | 32635 | 62538 | 33757 | 61679 | 155419 | 188593 | 86649 | |
VA | 522672 | 887525 | 413004 | 768475 | 2203286 | 2033550 | 940577 | |
WA | 433119 | 750274 | 357782 | 610378 | 1850983 | 1762811 | 783877 | |
WV | 105435 | 189649 | 91074 | 157989 | 470749 | 514505 | 285067 | |
WI | 362277 | 640286 | 311849 | 553914 | 1487457 | 1522038 | 750146 | |
WY | 38253 | 60890 | 29314 | 53980 | 137338 | 147279 | 65614 |
Hi KatiRG,
I have been trying to modify your code in order to make a horizontal version of your visualization. Yet have had no success after several attempts. Is it possible for you to upload a version like this please?
Thanks!
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hello,
I am trying to create a stacked graph similar to this one and have tried building it off of your original design. One difference is that I am trying to allow for multiple selections from the legend instead of just one. So far I have had a bit of trouble with this. Do you have any tips or samples that could point me in the right direction? I'd be happy to share my changes at this point for you to get a feel for the direction I am taking.
Thanks!