This example demonstrates how to use d3.dispatch to coordinate views with shared state. Here, a bar chart and a pie chart show two views of the same data: population by age group for a given state. (For brevity, a legend is omitted.) This example uses two custom events: a load event when data is available, and a statechange event when the displayed state is changed.
Custom events allow loose coupling of components: views can listen for events and update the DOM accordingly, without needing to explicitly tie each view together. When the drop-down menu changes, a statechange event is triggered which causes any interested listeners to be notified.
Each view uses a unique name, such as “bar” or “pie”, so that multiple listeners can be notified for a single event. (D3 requires listeners to be named, rather than anonymous, so that it’s easier to remove or inspect registered listeners.) Thus, the “load.menu”, “load.bar” and “load.pie” listeners are all notified when the data is loaded.
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.axis text {
font: 10px sans-serif;
}
.axis line,
.axis path {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
</style>
<body>
<script src="//d3js.org/d3.v4.min.js"></script>
<script>
var dispatch = d3.dispatch("load", "statechange");
var groups = [
"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"
];
d3.csv("data.csv", type, function(error, states) {
if (error) throw error;
var stateById = d3.map();
states.forEach(function(d) { stateById.set(d.id, d); });
dispatch.call("load", this, stateById);
dispatch.call("statechange", this, stateById.get("CA"));
});
// A drop-down menu for selecting a state; uses the "menu" namespace.
dispatch.on("load.menu", function(stateById) {
var select = d3.select("body")
.append("div")
.append("select")
.on("change", function() { dispatch.call("statechange", this, stateById.get(this.value)); });
select.selectAll("option")
.data(stateById.values())
.enter().append("option")
.attr("value", function(d) { return d.id; })
.text(function(d) { return d.id; });
dispatch.on("statechange.menu", function(state) {
select.property("value", state.id);
});
});
// A bar chart to show total population; uses the "bar" namespace.
dispatch.on("load.bar", function(stateById) {
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 80 - margin.left - margin.right,
height = 460 - margin.top - margin.bottom;
var y = d3.scaleLinear()
.domain([0, d3.max(stateById.values(), function(d) { return d.total; })])
.rangeRound([height, 0])
.nice();
var yAxis = d3.axisLeft(y)
.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 + ")");
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
var rect = svg.append("rect")
.attr("x", 4)
.attr("width", width - 4)
.attr("y", height)
.attr("height", 0)
.style("fill", "#aaa");
dispatch.on("statechange.bar", function(d) {
rect.transition()
.attr("y", y(d.total))
.attr("height", y(0) - y(d.total));
});
});
// A pie chart to show population by age group; uses the "pie" namespace.
dispatch.on("load.pie", function(stateById) {
var width = 880,
height = 460,
radius = Math.min(width, height) / 2;
var color = d3.scaleOrdinal()
.domain(groups)
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
var arc = d3.arc()
.outerRadius(radius - 10)
.innerRadius(radius - 70);
var pie = d3.pie()
.sort(null);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var path = svg.selectAll("path")
.data(groups)
.enter().append("path")
.style("fill", color)
.each(function() { this._current = {startAngle: 0, endAngle: 0}; });
dispatch.on("statechange.pie", function(d) {
path.data(pie.value(function(g) { return d[g]; })(groups)).transition()
.attrTween("d", function(d) {
var interpolate = d3.interpolate(this._current, d);
this._current = interpolate(0);
return function(t) {
return arc(interpolate(t));
};
});
});
});
// Coerce population counts to numbers and compute total per state.
function type(d) {
d.total = d3.sum(groups, function(k) { return d[k] = +d[k]; });
return d;
}
</script>
id,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