Update multiple charts simultaneously in d3.js
Clash Royale CLAN TAG#URR8PPP
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;
up vote
2
down vote
favorite
I've created 3 different charts all drawn from the same dataset,
my reasoning for building it this way is so that it'll be easier to add and remove additional rows of charts.
What I'm mainly interested in knowing is if the code is well structured and/or if it's idiomatic d3 code.
Here's a link to all the code: Plunker
And here's the same code but with hardcoded data:
var durations = 0;
var z = d3.scaleOrdinal()
.range(["orange", "steelblue"]);
var margin =
top: 35, right: 35, bottom: 35, left: 35, pad: 25
;
var csvData =
`AgeRange,female,male,Woman_one,Man_one,Woman_two,Man_two,Woman_three,Man_three
"17 - 19",50,36,23,22,3,0,5,5
"20 - 24",145,99,80,72,22,3,27,14
"25 - 29",123,109,40,80,28,3,42,22
"30 - 34",121,52,54,35,21,2,32,13
"35 - 39",88,65,23,30,15,4,44,28
"40 - 44",79,52,28,22,8,4,40,23
"45 - 49",89,51,21,27,14,1,47,20
"50 - 54",67,31,15,12,10,1,38,15
"55 - 59",55,25,7,3,7,3,39,17
"60 - 64",40,21,5,5,4,2,30,14
"65 - 69",26,11,1,0,1,0,22,11
"70 - 74",10,6,0,0,0,0,9,5
"75 +",9,1,0,0,0,0,9,1`;
var dataSet = d3.csvParse(csvData)
row1(dataSet);
function row1(data)
var check = false;
update(check);
function update(check)
var team = d3.selectAll(".select1").property("value")
data.forEach(function(d, i, columns)
d.male = +(d.male);
d.female = +(d.female);
d["Woman" + team] = +d["Woman" + team];
d["Man" + team] = +d["Man" + team];
return d;
)
if (check)
pyramid.update(data, team)
percent.update(data, team)
totals.update(data, team)
else
pyramid(data, team);
percent(data, team);
totals(data, team);
d3.selectAll(".select1").on("change", function()
check = true;
durations = 750;
update(check);
)
function pyramid(data, team)
var width = 550 - (margin.left + margin.right);
var height = 420 - (margin.top + margin.bottom);
var x = d3.scaleLinear()
.rangeRound([(width/2) + margin.pad, width]),
x2 = d3.scaleLinear()
.rangeRound([(width/2) - margin.pad, 0]),
y = d3.scaleBand()
.rangeRound([height, 0]).padding(0.1);
var xAxis = d3.axisBottom(x).ticks(6)
.tickSize(-height),
xAxis2 = d3.axisBottom(x2).ticks(6)
.tickSize(-height),
yAxis = d3.axisLeft(y);
var svg = d3.select("#row1").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", "axis axis--x hide")
.attr("transform", "translate(0," + height + ")")
svg.append("g")
.attr("class", "axis axis--x2 hide")
.attr("transform", "translate(0," + height + ")")
svg.append("g")
.attr("class", "axis axis--y")
.attr("transform", "translate(" + (width/2 + 10) + ",0)")
svg.append("text")
.attr("x", width/2)
.attr("y", 0)
.attr("text-anchor", "middle")
.text("Age");
update(data, team);
function update(data, team)
x.domain([0, d3.max(data,
d => Math.max(d.male, d.female))
]).nice();
x2.domain(x.domain())
y.domain(data.map(d => d.AgeRange));
svg.selectAll(".axis.axis--x")
.call(xAxis);
svg.selectAll(".axis.axis--x2")
.call(xAxis2);
svg.selectAll(".axis.axis--y")
.call(customYAxis);
function customYAxis(g)
g.call(yAxis);
g.selectAll("text").style("text-anchor", "middle")
g.select(".domain").remove();
g.selectAll("line").remove();
// ==== Men bar ====
var menTtl = svg.selectAll(".menTtl")
.data(data).enter()
.insert("g", ".axis--x")
.append("rect")
.attr("class", "M menTtl")
.attr("opacity",.6)
.attr("x", x(0))
.attr("y", d => y(d.AgeRange))
.attr("height", y.bandwidth())
.attr("width", d => Math.abs(x(d.male) - x(0)));
var menTeam = svg.selectAll(".menTeam")
.data(data);
menTeam = menTeam
.enter()
.insert("g", ".axis--x")
.append("rect")
.attr("class", "M menTeam")
.attr("x", x(0))
.attr("y", d => y(d.AgeRange))
.attr("height", y.bandwidth())
.merge(menTeam)
menTeam.transition().duration(durations)
.attr("width", d => Math.abs(x(d["Man" + team]) - x(0)));
// ==== Women bar ====
var womenTtl = svg.selectAll(".womenTtl")
.data(data).enter()
.append("rect")
.attr("class", "W womenTtl")
.attr("opacity",.6)
.attr("y", d => y(d.AgeRange))
.attr("x", d => x2(d.female))
.attr("height", y.bandwidth())
.attr("width", d => Math.abs(x2(d.female) - x2(0)));
var womenTeam = svg.selectAll(".womenTeam")
.data(data);
womenTeam = womenTeam
.enter()
.append("rect")
.attr("class", "W womenTeam")
.attr("y", d => y(d.AgeRange))
.attr("height", y.bandwidth())
.merge(womenTeam)
womenTeam.transition().duration(durations)
.attr("x", d => x2(d["Woman" + team]))
.attr("width", d => Math.abs(x2(d["Woman" + team]) - x2(0)));
pyramid.update = update;
function percent(data, team)
var width = 150 - (margin.left + margin.right),
height = 420 - (margin.top + margin.bottom);
var x0 = d3.scaleBand().rangeRound([0, width]),
x1 = d3.scaleBand().paddingOuter(0.2),
x2 = d3.scaleBand().paddingOuter(0.2),
y = d3.scaleLinear().rangeRound([height, 0]);
var xAxis = d3.axisBottom(x0),
yAxis = d3.axisLeft(y);
var svg = d3.select("#row1").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", "axis axis--x")
.attr("transform", "translate(0," + height + ")");
svg.append("g")
.attr("class", "axis axis--y");
update(data, team);
function update(data, team)
var ttlKey = data.columns.slice(1, 3);
var teamKey = ["Woman" + team, "Man" + team]
x0.domain(["Women & Men"]);
x1.domain(ttlKey).rangeRound([0, x0.bandwidth()]);
x2.domain(teamKey).rangeRound([0, x0.bandwidth()]);
y.domain([0, d3.sum(data, function(d)
return d3.max(ttlKey, key => d[key]);
)]).nice();
svg.selectAll(".axis.axis--y")
.call(yAxis);
svg.selectAll(".axis.axis--x")
.call(xAxis);
var barGroups = svg.selectAll("g.layer")
.data(["empty"]);
barGroups.exit().remove();
barGroups = barGroups
.enter()
.append("g")
.classed('layer', true);
var bars = svg.selectAll("g.layer").selectAll(".bars")
.data(function()
return ttlKey.map(function(key)
var sum = d3.sum(data, e => e[key] )
return key: key, value: sum;
);
).enter()
.append("rect")
.attr("class", "bars")
.attr("fill", d => z(d.key))
.attr("opacity", .6)
.attr("x", d => x1(d.key))
.attr("y", d => y(d.value))
.attr("width", x1.bandwidth())
.attr("height", d => height - y(d.value));
var barsTeam = svg.selectAll("g.layer").selectAll(".barsTeam")
.data(function()
return teamKey.map(function(key)
var sum = d3.sum(data, e => e[key] )
return key: key, value: sum;
);
);
barsTeam.exit().remove();
barsTeam = barsTeam
.enter()
.append("rect")
.attr("class", "barsTeam")
.attr("fill", d => z(d.key))
.attr("x", d => x2(d.key))
.attr("width", x1.bandwidth())
.merge(barsTeam);
barsTeam.transition().duration(durations)
.attr("y", d => y(d.value))
.attr("height", d => height - y(d.value));
percent.update = update;
function totals(data, team)
var width = 110 - (margin.left + margin.right),
height = 420 - (margin.top + margin.bottom);
var x = d3.scaleBand().rangeRound([0, width]).padding(0.1),
y = d3.scaleLinear().rangeRound([height, 0]);
var xAxis = d3.axisBottom(x),
yAxis = d3.axisLeft(y);
var svg = d3.select("#row1").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", "axis axis--x")
.attr("transform", "translate(0," + height + ")");
svg.append("g")
.attr("class", "axis axis--y");
update(data, team);
function update(data, team)
var sum = d3.sum(data, d => d3.sum([d.male, d.female]));
var sumTeam = d3.sum(data, function(d)
return d3.sum([d["Woman" + team], d["Man" + team]])
);
y.domain([0, sum]).nice();
x.domain(["Total"]);
svg.selectAll(".axis.axis--y")
.call(yAxis);
svg.selectAll(".axis.axis--x")
.call(xAxis);
var ttlBar = svg.selectAll(".ttlBar")
.data(["empty"]).enter()
.append("rect")
.attr("class", "ttlBar")
.attr("fill", "#ccc")
.attr("x", x(["Total"]))
.attr("y", y(sum))
.attr("width", x.bandwidth())
.attr("height", height - y(sum));
var ttlTeam = svg.selectAll(".ttlTeam")
.data(["empty"]);
ttlTeam = ttlTeam
.enter()
.append("rect")
.attr("class", "ttlTeam")
.attr("fill", "#999")
.attr("x", x(["Total"]))
.attr("width", x.bandwidth())
.merge(ttlTeam);
ttlTeam.transition().duration(durations)
.attr("height", height - y(sumTeam))
.attr("y", y(sumTeam))
totals.update = update
body
font: 12px arial;
margin: auto;
width: 850px;
padding-top:25px;
select
border: 1px solid #fff;
border-bottom: 1px solid #ccc;
cursor: pointer;
.M fill: steelblue;
.W fill: orange;
.hide path
display: none;
.hide .tick:not(:first-of-type) line
opacity: .25;
<meta charset="utf-8">
<script src="https://d3js.org/d3.v5.min.js"></script>
<div style="margin-left: 25px;">
<b>Choose Team:</b>
<select class="select1">
<option value="_one">Team 1</option>
<option value="_two">Team 2</option>
<option value="_three">Team 3</option>
</select>
</div>
<div id="row1"></div>
javascript d3.js
add a comment |Â
up vote
2
down vote
favorite
I've created 3 different charts all drawn from the same dataset,
my reasoning for building it this way is so that it'll be easier to add and remove additional rows of charts.
What I'm mainly interested in knowing is if the code is well structured and/or if it's idiomatic d3 code.
Here's a link to all the code: Plunker
And here's the same code but with hardcoded data:
var durations = 0;
var z = d3.scaleOrdinal()
.range(["orange", "steelblue"]);
var margin =
top: 35, right: 35, bottom: 35, left: 35, pad: 25
;
var csvData =
`AgeRange,female,male,Woman_one,Man_one,Woman_two,Man_two,Woman_three,Man_three
"17 - 19",50,36,23,22,3,0,5,5
"20 - 24",145,99,80,72,22,3,27,14
"25 - 29",123,109,40,80,28,3,42,22
"30 - 34",121,52,54,35,21,2,32,13
"35 - 39",88,65,23,30,15,4,44,28
"40 - 44",79,52,28,22,8,4,40,23
"45 - 49",89,51,21,27,14,1,47,20
"50 - 54",67,31,15,12,10,1,38,15
"55 - 59",55,25,7,3,7,3,39,17
"60 - 64",40,21,5,5,4,2,30,14
"65 - 69",26,11,1,0,1,0,22,11
"70 - 74",10,6,0,0,0,0,9,5
"75 +",9,1,0,0,0,0,9,1`;
var dataSet = d3.csvParse(csvData)
row1(dataSet);
function row1(data)
var check = false;
update(check);
function update(check)
var team = d3.selectAll(".select1").property("value")
data.forEach(function(d, i, columns)
d.male = +(d.male);
d.female = +(d.female);
d["Woman" + team] = +d["Woman" + team];
d["Man" + team] = +d["Man" + team];
return d;
)
if (check)
pyramid.update(data, team)
percent.update(data, team)
totals.update(data, team)
else
pyramid(data, team);
percent(data, team);
totals(data, team);
d3.selectAll(".select1").on("change", function()
check = true;
durations = 750;
update(check);
)
function pyramid(data, team)
var width = 550 - (margin.left + margin.right);
var height = 420 - (margin.top + margin.bottom);
var x = d3.scaleLinear()
.rangeRound([(width/2) + margin.pad, width]),
x2 = d3.scaleLinear()
.rangeRound([(width/2) - margin.pad, 0]),
y = d3.scaleBand()
.rangeRound([height, 0]).padding(0.1);
var xAxis = d3.axisBottom(x).ticks(6)
.tickSize(-height),
xAxis2 = d3.axisBottom(x2).ticks(6)
.tickSize(-height),
yAxis = d3.axisLeft(y);
var svg = d3.select("#row1").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", "axis axis--x hide")
.attr("transform", "translate(0," + height + ")")
svg.append("g")
.attr("class", "axis axis--x2 hide")
.attr("transform", "translate(0," + height + ")")
svg.append("g")
.attr("class", "axis axis--y")
.attr("transform", "translate(" + (width/2 + 10) + ",0)")
svg.append("text")
.attr("x", width/2)
.attr("y", 0)
.attr("text-anchor", "middle")
.text("Age");
update(data, team);
function update(data, team)
x.domain([0, d3.max(data,
d => Math.max(d.male, d.female))
]).nice();
x2.domain(x.domain())
y.domain(data.map(d => d.AgeRange));
svg.selectAll(".axis.axis--x")
.call(xAxis);
svg.selectAll(".axis.axis--x2")
.call(xAxis2);
svg.selectAll(".axis.axis--y")
.call(customYAxis);
function customYAxis(g)
g.call(yAxis);
g.selectAll("text").style("text-anchor", "middle")
g.select(".domain").remove();
g.selectAll("line").remove();
// ==== Men bar ====
var menTtl = svg.selectAll(".menTtl")
.data(data).enter()
.insert("g", ".axis--x")
.append("rect")
.attr("class", "M menTtl")
.attr("opacity",.6)
.attr("x", x(0))
.attr("y", d => y(d.AgeRange))
.attr("height", y.bandwidth())
.attr("width", d => Math.abs(x(d.male) - x(0)));
var menTeam = svg.selectAll(".menTeam")
.data(data);
menTeam = menTeam
.enter()
.insert("g", ".axis--x")
.append("rect")
.attr("class", "M menTeam")
.attr("x", x(0))
.attr("y", d => y(d.AgeRange))
.attr("height", y.bandwidth())
.merge(menTeam)
menTeam.transition().duration(durations)
.attr("width", d => Math.abs(x(d["Man" + team]) - x(0)));
// ==== Women bar ====
var womenTtl = svg.selectAll(".womenTtl")
.data(data).enter()
.append("rect")
.attr("class", "W womenTtl")
.attr("opacity",.6)
.attr("y", d => y(d.AgeRange))
.attr("x", d => x2(d.female))
.attr("height", y.bandwidth())
.attr("width", d => Math.abs(x2(d.female) - x2(0)));
var womenTeam = svg.selectAll(".womenTeam")
.data(data);
womenTeam = womenTeam
.enter()
.append("rect")
.attr("class", "W womenTeam")
.attr("y", d => y(d.AgeRange))
.attr("height", y.bandwidth())
.merge(womenTeam)
womenTeam.transition().duration(durations)
.attr("x", d => x2(d["Woman" + team]))
.attr("width", d => Math.abs(x2(d["Woman" + team]) - x2(0)));
pyramid.update = update;
function percent(data, team)
var width = 150 - (margin.left + margin.right),
height = 420 - (margin.top + margin.bottom);
var x0 = d3.scaleBand().rangeRound([0, width]),
x1 = d3.scaleBand().paddingOuter(0.2),
x2 = d3.scaleBand().paddingOuter(0.2),
y = d3.scaleLinear().rangeRound([height, 0]);
var xAxis = d3.axisBottom(x0),
yAxis = d3.axisLeft(y);
var svg = d3.select("#row1").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", "axis axis--x")
.attr("transform", "translate(0," + height + ")");
svg.append("g")
.attr("class", "axis axis--y");
update(data, team);
function update(data, team)
var ttlKey = data.columns.slice(1, 3);
var teamKey = ["Woman" + team, "Man" + team]
x0.domain(["Women & Men"]);
x1.domain(ttlKey).rangeRound([0, x0.bandwidth()]);
x2.domain(teamKey).rangeRound([0, x0.bandwidth()]);
y.domain([0, d3.sum(data, function(d)
return d3.max(ttlKey, key => d[key]);
)]).nice();
svg.selectAll(".axis.axis--y")
.call(yAxis);
svg.selectAll(".axis.axis--x")
.call(xAxis);
var barGroups = svg.selectAll("g.layer")
.data(["empty"]);
barGroups.exit().remove();
barGroups = barGroups
.enter()
.append("g")
.classed('layer', true);
var bars = svg.selectAll("g.layer").selectAll(".bars")
.data(function()
return ttlKey.map(function(key)
var sum = d3.sum(data, e => e[key] )
return key: key, value: sum;
);
).enter()
.append("rect")
.attr("class", "bars")
.attr("fill", d => z(d.key))
.attr("opacity", .6)
.attr("x", d => x1(d.key))
.attr("y", d => y(d.value))
.attr("width", x1.bandwidth())
.attr("height", d => height - y(d.value));
var barsTeam = svg.selectAll("g.layer").selectAll(".barsTeam")
.data(function()
return teamKey.map(function(key)
var sum = d3.sum(data, e => e[key] )
return key: key, value: sum;
);
);
barsTeam.exit().remove();
barsTeam = barsTeam
.enter()
.append("rect")
.attr("class", "barsTeam")
.attr("fill", d => z(d.key))
.attr("x", d => x2(d.key))
.attr("width", x1.bandwidth())
.merge(barsTeam);
barsTeam.transition().duration(durations)
.attr("y", d => y(d.value))
.attr("height", d => height - y(d.value));
percent.update = update;
function totals(data, team)
var width = 110 - (margin.left + margin.right),
height = 420 - (margin.top + margin.bottom);
var x = d3.scaleBand().rangeRound([0, width]).padding(0.1),
y = d3.scaleLinear().rangeRound([height, 0]);
var xAxis = d3.axisBottom(x),
yAxis = d3.axisLeft(y);
var svg = d3.select("#row1").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", "axis axis--x")
.attr("transform", "translate(0," + height + ")");
svg.append("g")
.attr("class", "axis axis--y");
update(data, team);
function update(data, team)
var sum = d3.sum(data, d => d3.sum([d.male, d.female]));
var sumTeam = d3.sum(data, function(d)
return d3.sum([d["Woman" + team], d["Man" + team]])
);
y.domain([0, sum]).nice();
x.domain(["Total"]);
svg.selectAll(".axis.axis--y")
.call(yAxis);
svg.selectAll(".axis.axis--x")
.call(xAxis);
var ttlBar = svg.selectAll(".ttlBar")
.data(["empty"]).enter()
.append("rect")
.attr("class", "ttlBar")
.attr("fill", "#ccc")
.attr("x", x(["Total"]))
.attr("y", y(sum))
.attr("width", x.bandwidth())
.attr("height", height - y(sum));
var ttlTeam = svg.selectAll(".ttlTeam")
.data(["empty"]);
ttlTeam = ttlTeam
.enter()
.append("rect")
.attr("class", "ttlTeam")
.attr("fill", "#999")
.attr("x", x(["Total"]))
.attr("width", x.bandwidth())
.merge(ttlTeam);
ttlTeam.transition().duration(durations)
.attr("height", height - y(sumTeam))
.attr("y", y(sumTeam))
totals.update = update
body
font: 12px arial;
margin: auto;
width: 850px;
padding-top:25px;
select
border: 1px solid #fff;
border-bottom: 1px solid #ccc;
cursor: pointer;
.M fill: steelblue;
.W fill: orange;
.hide path
display: none;
.hide .tick:not(:first-of-type) line
opacity: .25;
<meta charset="utf-8">
<script src="https://d3js.org/d3.v5.min.js"></script>
<div style="margin-left: 25px;">
<b>Choose Team:</b>
<select class="select1">
<option value="_one">Team 1</option>
<option value="_two">Team 2</option>
<option value="_three">Team 3</option>
</select>
</div>
<div id="row1"></div>
javascript d3.js
add a comment |Â
up vote
2
down vote
favorite
up vote
2
down vote
favorite
I've created 3 different charts all drawn from the same dataset,
my reasoning for building it this way is so that it'll be easier to add and remove additional rows of charts.
What I'm mainly interested in knowing is if the code is well structured and/or if it's idiomatic d3 code.
Here's a link to all the code: Plunker
And here's the same code but with hardcoded data:
var durations = 0;
var z = d3.scaleOrdinal()
.range(["orange", "steelblue"]);
var margin =
top: 35, right: 35, bottom: 35, left: 35, pad: 25
;
var csvData =
`AgeRange,female,male,Woman_one,Man_one,Woman_two,Man_two,Woman_three,Man_three
"17 - 19",50,36,23,22,3,0,5,5
"20 - 24",145,99,80,72,22,3,27,14
"25 - 29",123,109,40,80,28,3,42,22
"30 - 34",121,52,54,35,21,2,32,13
"35 - 39",88,65,23,30,15,4,44,28
"40 - 44",79,52,28,22,8,4,40,23
"45 - 49",89,51,21,27,14,1,47,20
"50 - 54",67,31,15,12,10,1,38,15
"55 - 59",55,25,7,3,7,3,39,17
"60 - 64",40,21,5,5,4,2,30,14
"65 - 69",26,11,1,0,1,0,22,11
"70 - 74",10,6,0,0,0,0,9,5
"75 +",9,1,0,0,0,0,9,1`;
var dataSet = d3.csvParse(csvData)
row1(dataSet);
function row1(data)
var check = false;
update(check);
function update(check)
var team = d3.selectAll(".select1").property("value")
data.forEach(function(d, i, columns)
d.male = +(d.male);
d.female = +(d.female);
d["Woman" + team] = +d["Woman" + team];
d["Man" + team] = +d["Man" + team];
return d;
)
if (check)
pyramid.update(data, team)
percent.update(data, team)
totals.update(data, team)
else
pyramid(data, team);
percent(data, team);
totals(data, team);
d3.selectAll(".select1").on("change", function()
check = true;
durations = 750;
update(check);
)
function pyramid(data, team)
var width = 550 - (margin.left + margin.right);
var height = 420 - (margin.top + margin.bottom);
var x = d3.scaleLinear()
.rangeRound([(width/2) + margin.pad, width]),
x2 = d3.scaleLinear()
.rangeRound([(width/2) - margin.pad, 0]),
y = d3.scaleBand()
.rangeRound([height, 0]).padding(0.1);
var xAxis = d3.axisBottom(x).ticks(6)
.tickSize(-height),
xAxis2 = d3.axisBottom(x2).ticks(6)
.tickSize(-height),
yAxis = d3.axisLeft(y);
var svg = d3.select("#row1").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", "axis axis--x hide")
.attr("transform", "translate(0," + height + ")")
svg.append("g")
.attr("class", "axis axis--x2 hide")
.attr("transform", "translate(0," + height + ")")
svg.append("g")
.attr("class", "axis axis--y")
.attr("transform", "translate(" + (width/2 + 10) + ",0)")
svg.append("text")
.attr("x", width/2)
.attr("y", 0)
.attr("text-anchor", "middle")
.text("Age");
update(data, team);
function update(data, team)
x.domain([0, d3.max(data,
d => Math.max(d.male, d.female))
]).nice();
x2.domain(x.domain())
y.domain(data.map(d => d.AgeRange));
svg.selectAll(".axis.axis--x")
.call(xAxis);
svg.selectAll(".axis.axis--x2")
.call(xAxis2);
svg.selectAll(".axis.axis--y")
.call(customYAxis);
function customYAxis(g)
g.call(yAxis);
g.selectAll("text").style("text-anchor", "middle")
g.select(".domain").remove();
g.selectAll("line").remove();
// ==== Men bar ====
var menTtl = svg.selectAll(".menTtl")
.data(data).enter()
.insert("g", ".axis--x")
.append("rect")
.attr("class", "M menTtl")
.attr("opacity",.6)
.attr("x", x(0))
.attr("y", d => y(d.AgeRange))
.attr("height", y.bandwidth())
.attr("width", d => Math.abs(x(d.male) - x(0)));
var menTeam = svg.selectAll(".menTeam")
.data(data);
menTeam = menTeam
.enter()
.insert("g", ".axis--x")
.append("rect")
.attr("class", "M menTeam")
.attr("x", x(0))
.attr("y", d => y(d.AgeRange))
.attr("height", y.bandwidth())
.merge(menTeam)
menTeam.transition().duration(durations)
.attr("width", d => Math.abs(x(d["Man" + team]) - x(0)));
// ==== Women bar ====
var womenTtl = svg.selectAll(".womenTtl")
.data(data).enter()
.append("rect")
.attr("class", "W womenTtl")
.attr("opacity",.6)
.attr("y", d => y(d.AgeRange))
.attr("x", d => x2(d.female))
.attr("height", y.bandwidth())
.attr("width", d => Math.abs(x2(d.female) - x2(0)));
var womenTeam = svg.selectAll(".womenTeam")
.data(data);
womenTeam = womenTeam
.enter()
.append("rect")
.attr("class", "W womenTeam")
.attr("y", d => y(d.AgeRange))
.attr("height", y.bandwidth())
.merge(womenTeam)
womenTeam.transition().duration(durations)
.attr("x", d => x2(d["Woman" + team]))
.attr("width", d => Math.abs(x2(d["Woman" + team]) - x2(0)));
pyramid.update = update;
function percent(data, team)
var width = 150 - (margin.left + margin.right),
height = 420 - (margin.top + margin.bottom);
var x0 = d3.scaleBand().rangeRound([0, width]),
x1 = d3.scaleBand().paddingOuter(0.2),
x2 = d3.scaleBand().paddingOuter(0.2),
y = d3.scaleLinear().rangeRound([height, 0]);
var xAxis = d3.axisBottom(x0),
yAxis = d3.axisLeft(y);
var svg = d3.select("#row1").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", "axis axis--x")
.attr("transform", "translate(0," + height + ")");
svg.append("g")
.attr("class", "axis axis--y");
update(data, team);
function update(data, team)
var ttlKey = data.columns.slice(1, 3);
var teamKey = ["Woman" + team, "Man" + team]
x0.domain(["Women & Men"]);
x1.domain(ttlKey).rangeRound([0, x0.bandwidth()]);
x2.domain(teamKey).rangeRound([0, x0.bandwidth()]);
y.domain([0, d3.sum(data, function(d)
return d3.max(ttlKey, key => d[key]);
)]).nice();
svg.selectAll(".axis.axis--y")
.call(yAxis);
svg.selectAll(".axis.axis--x")
.call(xAxis);
var barGroups = svg.selectAll("g.layer")
.data(["empty"]);
barGroups.exit().remove();
barGroups = barGroups
.enter()
.append("g")
.classed('layer', true);
var bars = svg.selectAll("g.layer").selectAll(".bars")
.data(function()
return ttlKey.map(function(key)
var sum = d3.sum(data, e => e[key] )
return key: key, value: sum;
);
).enter()
.append("rect")
.attr("class", "bars")
.attr("fill", d => z(d.key))
.attr("opacity", .6)
.attr("x", d => x1(d.key))
.attr("y", d => y(d.value))
.attr("width", x1.bandwidth())
.attr("height", d => height - y(d.value));
var barsTeam = svg.selectAll("g.layer").selectAll(".barsTeam")
.data(function()
return teamKey.map(function(key)
var sum = d3.sum(data, e => e[key] )
return key: key, value: sum;
);
);
barsTeam.exit().remove();
barsTeam = barsTeam
.enter()
.append("rect")
.attr("class", "barsTeam")
.attr("fill", d => z(d.key))
.attr("x", d => x2(d.key))
.attr("width", x1.bandwidth())
.merge(barsTeam);
barsTeam.transition().duration(durations)
.attr("y", d => y(d.value))
.attr("height", d => height - y(d.value));
percent.update = update;
function totals(data, team)
var width = 110 - (margin.left + margin.right),
height = 420 - (margin.top + margin.bottom);
var x = d3.scaleBand().rangeRound([0, width]).padding(0.1),
y = d3.scaleLinear().rangeRound([height, 0]);
var xAxis = d3.axisBottom(x),
yAxis = d3.axisLeft(y);
var svg = d3.select("#row1").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", "axis axis--x")
.attr("transform", "translate(0," + height + ")");
svg.append("g")
.attr("class", "axis axis--y");
update(data, team);
function update(data, team)
var sum = d3.sum(data, d => d3.sum([d.male, d.female]));
var sumTeam = d3.sum(data, function(d)
return d3.sum([d["Woman" + team], d["Man" + team]])
);
y.domain([0, sum]).nice();
x.domain(["Total"]);
svg.selectAll(".axis.axis--y")
.call(yAxis);
svg.selectAll(".axis.axis--x")
.call(xAxis);
var ttlBar = svg.selectAll(".ttlBar")
.data(["empty"]).enter()
.append("rect")
.attr("class", "ttlBar")
.attr("fill", "#ccc")
.attr("x", x(["Total"]))
.attr("y", y(sum))
.attr("width", x.bandwidth())
.attr("height", height - y(sum));
var ttlTeam = svg.selectAll(".ttlTeam")
.data(["empty"]);
ttlTeam = ttlTeam
.enter()
.append("rect")
.attr("class", "ttlTeam")
.attr("fill", "#999")
.attr("x", x(["Total"]))
.attr("width", x.bandwidth())
.merge(ttlTeam);
ttlTeam.transition().duration(durations)
.attr("height", height - y(sumTeam))
.attr("y", y(sumTeam))
totals.update = update
body
font: 12px arial;
margin: auto;
width: 850px;
padding-top:25px;
select
border: 1px solid #fff;
border-bottom: 1px solid #ccc;
cursor: pointer;
.M fill: steelblue;
.W fill: orange;
.hide path
display: none;
.hide .tick:not(:first-of-type) line
opacity: .25;
<meta charset="utf-8">
<script src="https://d3js.org/d3.v5.min.js"></script>
<div style="margin-left: 25px;">
<b>Choose Team:</b>
<select class="select1">
<option value="_one">Team 1</option>
<option value="_two">Team 2</option>
<option value="_three">Team 3</option>
</select>
</div>
<div id="row1"></div>
javascript d3.js
I've created 3 different charts all drawn from the same dataset,
my reasoning for building it this way is so that it'll be easier to add and remove additional rows of charts.
What I'm mainly interested in knowing is if the code is well structured and/or if it's idiomatic d3 code.
Here's a link to all the code: Plunker
And here's the same code but with hardcoded data:
var durations = 0;
var z = d3.scaleOrdinal()
.range(["orange", "steelblue"]);
var margin =
top: 35, right: 35, bottom: 35, left: 35, pad: 25
;
var csvData =
`AgeRange,female,male,Woman_one,Man_one,Woman_two,Man_two,Woman_three,Man_three
"17 - 19",50,36,23,22,3,0,5,5
"20 - 24",145,99,80,72,22,3,27,14
"25 - 29",123,109,40,80,28,3,42,22
"30 - 34",121,52,54,35,21,2,32,13
"35 - 39",88,65,23,30,15,4,44,28
"40 - 44",79,52,28,22,8,4,40,23
"45 - 49",89,51,21,27,14,1,47,20
"50 - 54",67,31,15,12,10,1,38,15
"55 - 59",55,25,7,3,7,3,39,17
"60 - 64",40,21,5,5,4,2,30,14
"65 - 69",26,11,1,0,1,0,22,11
"70 - 74",10,6,0,0,0,0,9,5
"75 +",9,1,0,0,0,0,9,1`;
var dataSet = d3.csvParse(csvData)
row1(dataSet);
function row1(data)
var check = false;
update(check);
function update(check)
var team = d3.selectAll(".select1").property("value")
data.forEach(function(d, i, columns)
d.male = +(d.male);
d.female = +(d.female);
d["Woman" + team] = +d["Woman" + team];
d["Man" + team] = +d["Man" + team];
return d;
)
if (check)
pyramid.update(data, team)
percent.update(data, team)
totals.update(data, team)
else
pyramid(data, team);
percent(data, team);
totals(data, team);
d3.selectAll(".select1").on("change", function()
check = true;
durations = 750;
update(check);
)
function pyramid(data, team)
var width = 550 - (margin.left + margin.right);
var height = 420 - (margin.top + margin.bottom);
var x = d3.scaleLinear()
.rangeRound([(width/2) + margin.pad, width]),
x2 = d3.scaleLinear()
.rangeRound([(width/2) - margin.pad, 0]),
y = d3.scaleBand()
.rangeRound([height, 0]).padding(0.1);
var xAxis = d3.axisBottom(x).ticks(6)
.tickSize(-height),
xAxis2 = d3.axisBottom(x2).ticks(6)
.tickSize(-height),
yAxis = d3.axisLeft(y);
var svg = d3.select("#row1").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", "axis axis--x hide")
.attr("transform", "translate(0," + height + ")")
svg.append("g")
.attr("class", "axis axis--x2 hide")
.attr("transform", "translate(0," + height + ")")
svg.append("g")
.attr("class", "axis axis--y")
.attr("transform", "translate(" + (width/2 + 10) + ",0)")
svg.append("text")
.attr("x", width/2)
.attr("y", 0)
.attr("text-anchor", "middle")
.text("Age");
update(data, team);
function update(data, team)
x.domain([0, d3.max(data,
d => Math.max(d.male, d.female))
]).nice();
x2.domain(x.domain())
y.domain(data.map(d => d.AgeRange));
svg.selectAll(".axis.axis--x")
.call(xAxis);
svg.selectAll(".axis.axis--x2")
.call(xAxis2);
svg.selectAll(".axis.axis--y")
.call(customYAxis);
function customYAxis(g)
g.call(yAxis);
g.selectAll("text").style("text-anchor", "middle")
g.select(".domain").remove();
g.selectAll("line").remove();
// ==== Men bar ====
var menTtl = svg.selectAll(".menTtl")
.data(data).enter()
.insert("g", ".axis--x")
.append("rect")
.attr("class", "M menTtl")
.attr("opacity",.6)
.attr("x", x(0))
.attr("y", d => y(d.AgeRange))
.attr("height", y.bandwidth())
.attr("width", d => Math.abs(x(d.male) - x(0)));
var menTeam = svg.selectAll(".menTeam")
.data(data);
menTeam = menTeam
.enter()
.insert("g", ".axis--x")
.append("rect")
.attr("class", "M menTeam")
.attr("x", x(0))
.attr("y", d => y(d.AgeRange))
.attr("height", y.bandwidth())
.merge(menTeam)
menTeam.transition().duration(durations)
.attr("width", d => Math.abs(x(d["Man" + team]) - x(0)));
// ==== Women bar ====
var womenTtl = svg.selectAll(".womenTtl")
.data(data).enter()
.append("rect")
.attr("class", "W womenTtl")
.attr("opacity",.6)
.attr("y", d => y(d.AgeRange))
.attr("x", d => x2(d.female))
.attr("height", y.bandwidth())
.attr("width", d => Math.abs(x2(d.female) - x2(0)));
var womenTeam = svg.selectAll(".womenTeam")
.data(data);
womenTeam = womenTeam
.enter()
.append("rect")
.attr("class", "W womenTeam")
.attr("y", d => y(d.AgeRange))
.attr("height", y.bandwidth())
.merge(womenTeam)
womenTeam.transition().duration(durations)
.attr("x", d => x2(d["Woman" + team]))
.attr("width", d => Math.abs(x2(d["Woman" + team]) - x2(0)));
pyramid.update = update;
function percent(data, team)
var width = 150 - (margin.left + margin.right),
height = 420 - (margin.top + margin.bottom);
var x0 = d3.scaleBand().rangeRound([0, width]),
x1 = d3.scaleBand().paddingOuter(0.2),
x2 = d3.scaleBand().paddingOuter(0.2),
y = d3.scaleLinear().rangeRound([height, 0]);
var xAxis = d3.axisBottom(x0),
yAxis = d3.axisLeft(y);
var svg = d3.select("#row1").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", "axis axis--x")
.attr("transform", "translate(0," + height + ")");
svg.append("g")
.attr("class", "axis axis--y");
update(data, team);
function update(data, team)
var ttlKey = data.columns.slice(1, 3);
var teamKey = ["Woman" + team, "Man" + team]
x0.domain(["Women & Men"]);
x1.domain(ttlKey).rangeRound([0, x0.bandwidth()]);
x2.domain(teamKey).rangeRound([0, x0.bandwidth()]);
y.domain([0, d3.sum(data, function(d)
return d3.max(ttlKey, key => d[key]);
)]).nice();
svg.selectAll(".axis.axis--y")
.call(yAxis);
svg.selectAll(".axis.axis--x")
.call(xAxis);
var barGroups = svg.selectAll("g.layer")
.data(["empty"]);
barGroups.exit().remove();
barGroups = barGroups
.enter()
.append("g")
.classed('layer', true);
var bars = svg.selectAll("g.layer").selectAll(".bars")
.data(function()
return ttlKey.map(function(key)
var sum = d3.sum(data, e => e[key] )
return key: key, value: sum;
);
).enter()
.append("rect")
.attr("class", "bars")
.attr("fill", d => z(d.key))
.attr("opacity", .6)
.attr("x", d => x1(d.key))
.attr("y", d => y(d.value))
.attr("width", x1.bandwidth())
.attr("height", d => height - y(d.value));
var barsTeam = svg.selectAll("g.layer").selectAll(".barsTeam")
.data(function()
return teamKey.map(function(key)
var sum = d3.sum(data, e => e[key] )
return key: key, value: sum;
);
);
barsTeam.exit().remove();
barsTeam = barsTeam
.enter()
.append("rect")
.attr("class", "barsTeam")
.attr("fill", d => z(d.key))
.attr("x", d => x2(d.key))
.attr("width", x1.bandwidth())
.merge(barsTeam);
barsTeam.transition().duration(durations)
.attr("y", d => y(d.value))
.attr("height", d => height - y(d.value));
percent.update = update;
function totals(data, team)
var width = 110 - (margin.left + margin.right),
height = 420 - (margin.top + margin.bottom);
var x = d3.scaleBand().rangeRound([0, width]).padding(0.1),
y = d3.scaleLinear().rangeRound([height, 0]);
var xAxis = d3.axisBottom(x),
yAxis = d3.axisLeft(y);
var svg = d3.select("#row1").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", "axis axis--x")
.attr("transform", "translate(0," + height + ")");
svg.append("g")
.attr("class", "axis axis--y");
update(data, team);
function update(data, team)
var sum = d3.sum(data, d => d3.sum([d.male, d.female]));
var sumTeam = d3.sum(data, function(d)
return d3.sum([d["Woman" + team], d["Man" + team]])
);
y.domain([0, sum]).nice();
x.domain(["Total"]);
svg.selectAll(".axis.axis--y")
.call(yAxis);
svg.selectAll(".axis.axis--x")
.call(xAxis);
var ttlBar = svg.selectAll(".ttlBar")
.data(["empty"]).enter()
.append("rect")
.attr("class", "ttlBar")
.attr("fill", "#ccc")
.attr("x", x(["Total"]))
.attr("y", y(sum))
.attr("width", x.bandwidth())
.attr("height", height - y(sum));
var ttlTeam = svg.selectAll(".ttlTeam")
.data(["empty"]);
ttlTeam = ttlTeam
.enter()
.append("rect")
.attr("class", "ttlTeam")
.attr("fill", "#999")
.attr("x", x(["Total"]))
.attr("width", x.bandwidth())
.merge(ttlTeam);
ttlTeam.transition().duration(durations)
.attr("height", height - y(sumTeam))
.attr("y", y(sumTeam))
totals.update = update
body
font: 12px arial;
margin: auto;
width: 850px;
padding-top:25px;
select
border: 1px solid #fff;
border-bottom: 1px solid #ccc;
cursor: pointer;
.M fill: steelblue;
.W fill: orange;
.hide path
display: none;
.hide .tick:not(:first-of-type) line
opacity: .25;
<meta charset="utf-8">
<script src="https://d3js.org/d3.v5.min.js"></script>
<div style="margin-left: 25px;">
<b>Choose Team:</b>
<select class="select1">
<option value="_one">Team 1</option>
<option value="_two">Team 2</option>
<option value="_three">Team 3</option>
</select>
</div>
<div id="row1"></div>
var durations = 0;
var z = d3.scaleOrdinal()
.range(["orange", "steelblue"]);
var margin =
top: 35, right: 35, bottom: 35, left: 35, pad: 25
;
var csvData =
`AgeRange,female,male,Woman_one,Man_one,Woman_two,Man_two,Woman_three,Man_three
"17 - 19",50,36,23,22,3,0,5,5
"20 - 24",145,99,80,72,22,3,27,14
"25 - 29",123,109,40,80,28,3,42,22
"30 - 34",121,52,54,35,21,2,32,13
"35 - 39",88,65,23,30,15,4,44,28
"40 - 44",79,52,28,22,8,4,40,23
"45 - 49",89,51,21,27,14,1,47,20
"50 - 54",67,31,15,12,10,1,38,15
"55 - 59",55,25,7,3,7,3,39,17
"60 - 64",40,21,5,5,4,2,30,14
"65 - 69",26,11,1,0,1,0,22,11
"70 - 74",10,6,0,0,0,0,9,5
"75 +",9,1,0,0,0,0,9,1`;
var dataSet = d3.csvParse(csvData)
row1(dataSet);
function row1(data)
var check = false;
update(check);
function update(check)
var team = d3.selectAll(".select1").property("value")
data.forEach(function(d, i, columns)
d.male = +(d.male);
d.female = +(d.female);
d["Woman" + team] = +d["Woman" + team];
d["Man" + team] = +d["Man" + team];
return d;
)
if (check)
pyramid.update(data, team)
percent.update(data, team)
totals.update(data, team)
else
pyramid(data, team);
percent(data, team);
totals(data, team);
d3.selectAll(".select1").on("change", function()
check = true;
durations = 750;
update(check);
)
function pyramid(data, team)
var width = 550 - (margin.left + margin.right);
var height = 420 - (margin.top + margin.bottom);
var x = d3.scaleLinear()
.rangeRound([(width/2) + margin.pad, width]),
x2 = d3.scaleLinear()
.rangeRound([(width/2) - margin.pad, 0]),
y = d3.scaleBand()
.rangeRound([height, 0]).padding(0.1);
var xAxis = d3.axisBottom(x).ticks(6)
.tickSize(-height),
xAxis2 = d3.axisBottom(x2).ticks(6)
.tickSize(-height),
yAxis = d3.axisLeft(y);
var svg = d3.select("#row1").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", "axis axis--x hide")
.attr("transform", "translate(0," + height + ")")
svg.append("g")
.attr("class", "axis axis--x2 hide")
.attr("transform", "translate(0," + height + ")")
svg.append("g")
.attr("class", "axis axis--y")
.attr("transform", "translate(" + (width/2 + 10) + ",0)")
svg.append("text")
.attr("x", width/2)
.attr("y", 0)
.attr("text-anchor", "middle")
.text("Age");
update(data, team);
function update(data, team)
x.domain([0, d3.max(data,
d => Math.max(d.male, d.female))
]).nice();
x2.domain(x.domain())
y.domain(data.map(d => d.AgeRange));
svg.selectAll(".axis.axis--x")
.call(xAxis);
svg.selectAll(".axis.axis--x2")
.call(xAxis2);
svg.selectAll(".axis.axis--y")
.call(customYAxis);
function customYAxis(g)
g.call(yAxis);
g.selectAll("text").style("text-anchor", "middle")
g.select(".domain").remove();
g.selectAll("line").remove();
// ==== Men bar ====
var menTtl = svg.selectAll(".menTtl")
.data(data).enter()
.insert("g", ".axis--x")
.append("rect")
.attr("class", "M menTtl")
.attr("opacity",.6)
.attr("x", x(0))
.attr("y", d => y(d.AgeRange))
.attr("height", y.bandwidth())
.attr("width", d => Math.abs(x(d.male) - x(0)));
var menTeam = svg.selectAll(".menTeam")
.data(data);
menTeam = menTeam
.enter()
.insert("g", ".axis--x")
.append("rect")
.attr("class", "M menTeam")
.attr("x", x(0))
.attr("y", d => y(d.AgeRange))
.attr("height", y.bandwidth())
.merge(menTeam)
menTeam.transition().duration(durations)
.attr("width", d => Math.abs(x(d["Man" + team]) - x(0)));
// ==== Women bar ====
var womenTtl = svg.selectAll(".womenTtl")
.data(data).enter()
.append("rect")
.attr("class", "W womenTtl")
.attr("opacity",.6)
.attr("y", d => y(d.AgeRange))
.attr("x", d => x2(d.female))
.attr("height", y.bandwidth())
.attr("width", d => Math.abs(x2(d.female) - x2(0)));
var womenTeam = svg.selectAll(".womenTeam")
.data(data);
womenTeam = womenTeam
.enter()
.append("rect")
.attr("class", "W womenTeam")
.attr("y", d => y(d.AgeRange))
.attr("height", y.bandwidth())
.merge(womenTeam)
womenTeam.transition().duration(durations)
.attr("x", d => x2(d["Woman" + team]))
.attr("width", d => Math.abs(x2(d["Woman" + team]) - x2(0)));
pyramid.update = update;
function percent(data, team)
var width = 150 - (margin.left + margin.right),
height = 420 - (margin.top + margin.bottom);
var x0 = d3.scaleBand().rangeRound([0, width]),
x1 = d3.scaleBand().paddingOuter(0.2),
x2 = d3.scaleBand().paddingOuter(0.2),
y = d3.scaleLinear().rangeRound([height, 0]);
var xAxis = d3.axisBottom(x0),
yAxis = d3.axisLeft(y);
var svg = d3.select("#row1").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", "axis axis--x")
.attr("transform", "translate(0," + height + ")");
svg.append("g")
.attr("class", "axis axis--y");
update(data, team);
function update(data, team)
var ttlKey = data.columns.slice(1, 3);
var teamKey = ["Woman" + team, "Man" + team]
x0.domain(["Women & Men"]);
x1.domain(ttlKey).rangeRound([0, x0.bandwidth()]);
x2.domain(teamKey).rangeRound([0, x0.bandwidth()]);
y.domain([0, d3.sum(data, function(d)
return d3.max(ttlKey, key => d[key]);
)]).nice();
svg.selectAll(".axis.axis--y")
.call(yAxis);
svg.selectAll(".axis.axis--x")
.call(xAxis);
var barGroups = svg.selectAll("g.layer")
.data(["empty"]);
barGroups.exit().remove();
barGroups = barGroups
.enter()
.append("g")
.classed('layer', true);
var bars = svg.selectAll("g.layer").selectAll(".bars")
.data(function()
return ttlKey.map(function(key)
var sum = d3.sum(data, e => e[key] )
return key: key, value: sum;
);
).enter()
.append("rect")
.attr("class", "bars")
.attr("fill", d => z(d.key))
.attr("opacity", .6)
.attr("x", d => x1(d.key))
.attr("y", d => y(d.value))
.attr("width", x1.bandwidth())
.attr("height", d => height - y(d.value));
var barsTeam = svg.selectAll("g.layer").selectAll(".barsTeam")
.data(function()
return teamKey.map(function(key)
var sum = d3.sum(data, e => e[key] )
return key: key, value: sum;
);
);
barsTeam.exit().remove();
barsTeam = barsTeam
.enter()
.append("rect")
.attr("class", "barsTeam")
.attr("fill", d => z(d.key))
.attr("x", d => x2(d.key))
.attr("width", x1.bandwidth())
.merge(barsTeam);
barsTeam.transition().duration(durations)
.attr("y", d => y(d.value))
.attr("height", d => height - y(d.value));
percent.update = update;
function totals(data, team)
var width = 110 - (margin.left + margin.right),
height = 420 - (margin.top + margin.bottom);
var x = d3.scaleBand().rangeRound([0, width]).padding(0.1),
y = d3.scaleLinear().rangeRound([height, 0]);
var xAxis = d3.axisBottom(x),
yAxis = d3.axisLeft(y);
var svg = d3.select("#row1").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", "axis axis--x")
.attr("transform", "translate(0," + height + ")");
svg.append("g")
.attr("class", "axis axis--y");
update(data, team);
function update(data, team)
var sum = d3.sum(data, d => d3.sum([d.male, d.female]));
var sumTeam = d3.sum(data, function(d)
return d3.sum([d["Woman" + team], d["Man" + team]])
);
y.domain([0, sum]).nice();
x.domain(["Total"]);
svg.selectAll(".axis.axis--y")
.call(yAxis);
svg.selectAll(".axis.axis--x")
.call(xAxis);
var ttlBar = svg.selectAll(".ttlBar")
.data(["empty"]).enter()
.append("rect")
.attr("class", "ttlBar")
.attr("fill", "#ccc")
.attr("x", x(["Total"]))
.attr("y", y(sum))
.attr("width", x.bandwidth())
.attr("height", height - y(sum));
var ttlTeam = svg.selectAll(".ttlTeam")
.data(["empty"]);
ttlTeam = ttlTeam
.enter()
.append("rect")
.attr("class", "ttlTeam")
.attr("fill", "#999")
.attr("x", x(["Total"]))
.attr("width", x.bandwidth())
.merge(ttlTeam);
ttlTeam.transition().duration(durations)
.attr("height", height - y(sumTeam))
.attr("y", y(sumTeam))
totals.update = update
body
font: 12px arial;
margin: auto;
width: 850px;
padding-top:25px;
select
border: 1px solid #fff;
border-bottom: 1px solid #ccc;
cursor: pointer;
.M fill: steelblue;
.W fill: orange;
.hide path
display: none;
.hide .tick:not(:first-of-type) line
opacity: .25;
<meta charset="utf-8">
<script src="https://d3js.org/d3.v5.min.js"></script>
<div style="margin-left: 25px;">
<b>Choose Team:</b>
<select class="select1">
<option value="_one">Team 1</option>
<option value="_two">Team 2</option>
<option value="_three">Team 3</option>
</select>
</div>
<div id="row1"></div>
var durations = 0;
var z = d3.scaleOrdinal()
.range(["orange", "steelblue"]);
var margin =
top: 35, right: 35, bottom: 35, left: 35, pad: 25
;
var csvData =
`AgeRange,female,male,Woman_one,Man_one,Woman_two,Man_two,Woman_three,Man_three
"17 - 19",50,36,23,22,3,0,5,5
"20 - 24",145,99,80,72,22,3,27,14
"25 - 29",123,109,40,80,28,3,42,22
"30 - 34",121,52,54,35,21,2,32,13
"35 - 39",88,65,23,30,15,4,44,28
"40 - 44",79,52,28,22,8,4,40,23
"45 - 49",89,51,21,27,14,1,47,20
"50 - 54",67,31,15,12,10,1,38,15
"55 - 59",55,25,7,3,7,3,39,17
"60 - 64",40,21,5,5,4,2,30,14
"65 - 69",26,11,1,0,1,0,22,11
"70 - 74",10,6,0,0,0,0,9,5
"75 +",9,1,0,0,0,0,9,1`;
var dataSet = d3.csvParse(csvData)
row1(dataSet);
function row1(data)
var check = false;
update(check);
function update(check)
var team = d3.selectAll(".select1").property("value")
data.forEach(function(d, i, columns)
d.male = +(d.male);
d.female = +(d.female);
d["Woman" + team] = +d["Woman" + team];
d["Man" + team] = +d["Man" + team];
return d;
)
if (check)
pyramid.update(data, team)
percent.update(data, team)
totals.update(data, team)
else
pyramid(data, team);
percent(data, team);
totals(data, team);
d3.selectAll(".select1").on("change", function()
check = true;
durations = 750;
update(check);
)
function pyramid(data, team)
var width = 550 - (margin.left + margin.right);
var height = 420 - (margin.top + margin.bottom);
var x = d3.scaleLinear()
.rangeRound([(width/2) + margin.pad, width]),
x2 = d3.scaleLinear()
.rangeRound([(width/2) - margin.pad, 0]),
y = d3.scaleBand()
.rangeRound([height, 0]).padding(0.1);
var xAxis = d3.axisBottom(x).ticks(6)
.tickSize(-height),
xAxis2 = d3.axisBottom(x2).ticks(6)
.tickSize(-height),
yAxis = d3.axisLeft(y);
var svg = d3.select("#row1").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", "axis axis--x hide")
.attr("transform", "translate(0," + height + ")")
svg.append("g")
.attr("class", "axis axis--x2 hide")
.attr("transform", "translate(0," + height + ")")
svg.append("g")
.attr("class", "axis axis--y")
.attr("transform", "translate(" + (width/2 + 10) + ",0)")
svg.append("text")
.attr("x", width/2)
.attr("y", 0)
.attr("text-anchor", "middle")
.text("Age");
update(data, team);
function update(data, team)
x.domain([0, d3.max(data,
d => Math.max(d.male, d.female))
]).nice();
x2.domain(x.domain())
y.domain(data.map(d => d.AgeRange));
svg.selectAll(".axis.axis--x")
.call(xAxis);
svg.selectAll(".axis.axis--x2")
.call(xAxis2);
svg.selectAll(".axis.axis--y")
.call(customYAxis);
function customYAxis(g)
g.call(yAxis);
g.selectAll("text").style("text-anchor", "middle")
g.select(".domain").remove();
g.selectAll("line").remove();
// ==== Men bar ====
var menTtl = svg.selectAll(".menTtl")
.data(data).enter()
.insert("g", ".axis--x")
.append("rect")
.attr("class", "M menTtl")
.attr("opacity",.6)
.attr("x", x(0))
.attr("y", d => y(d.AgeRange))
.attr("height", y.bandwidth())
.attr("width", d => Math.abs(x(d.male) - x(0)));
var menTeam = svg.selectAll(".menTeam")
.data(data);
menTeam = menTeam
.enter()
.insert("g", ".axis--x")
.append("rect")
.attr("class", "M menTeam")
.attr("x", x(0))
.attr("y", d => y(d.AgeRange))
.attr("height", y.bandwidth())
.merge(menTeam)
menTeam.transition().duration(durations)
.attr("width", d => Math.abs(x(d["Man" + team]) - x(0)));
// ==== Women bar ====
var womenTtl = svg.selectAll(".womenTtl")
.data(data).enter()
.append("rect")
.attr("class", "W womenTtl")
.attr("opacity",.6)
.attr("y", d => y(d.AgeRange))
.attr("x", d => x2(d.female))
.attr("height", y.bandwidth())
.attr("width", d => Math.abs(x2(d.female) - x2(0)));
var womenTeam = svg.selectAll(".womenTeam")
.data(data);
womenTeam = womenTeam
.enter()
.append("rect")
.attr("class", "W womenTeam")
.attr("y", d => y(d.AgeRange))
.attr("height", y.bandwidth())
.merge(womenTeam)
womenTeam.transition().duration(durations)
.attr("x", d => x2(d["Woman" + team]))
.attr("width", d => Math.abs(x2(d["Woman" + team]) - x2(0)));
pyramid.update = update;
function percent(data, team)
var width = 150 - (margin.left + margin.right),
height = 420 - (margin.top + margin.bottom);
var x0 = d3.scaleBand().rangeRound([0, width]),
x1 = d3.scaleBand().paddingOuter(0.2),
x2 = d3.scaleBand().paddingOuter(0.2),
y = d3.scaleLinear().rangeRound([height, 0]);
var xAxis = d3.axisBottom(x0),
yAxis = d3.axisLeft(y);
var svg = d3.select("#row1").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", "axis axis--x")
.attr("transform", "translate(0," + height + ")");
svg.append("g")
.attr("class", "axis axis--y");
update(data, team);
function update(data, team)
var ttlKey = data.columns.slice(1, 3);
var teamKey = ["Woman" + team, "Man" + team]
x0.domain(["Women & Men"]);
x1.domain(ttlKey).rangeRound([0, x0.bandwidth()]);
x2.domain(teamKey).rangeRound([0, x0.bandwidth()]);
y.domain([0, d3.sum(data, function(d)
return d3.max(ttlKey, key => d[key]);
)]).nice();
svg.selectAll(".axis.axis--y")
.call(yAxis);
svg.selectAll(".axis.axis--x")
.call(xAxis);
var barGroups = svg.selectAll("g.layer")
.data(["empty"]);
barGroups.exit().remove();
barGroups = barGroups
.enter()
.append("g")
.classed('layer', true);
var bars = svg.selectAll("g.layer").selectAll(".bars")
.data(function()
return ttlKey.map(function(key)
var sum = d3.sum(data, e => e[key] )
return key: key, value: sum;
);
).enter()
.append("rect")
.attr("class", "bars")
.attr("fill", d => z(d.key))
.attr("opacity", .6)
.attr("x", d => x1(d.key))
.attr("y", d => y(d.value))
.attr("width", x1.bandwidth())
.attr("height", d => height - y(d.value));
var barsTeam = svg.selectAll("g.layer").selectAll(".barsTeam")
.data(function()
return teamKey.map(function(key)
var sum = d3.sum(data, e => e[key] )
return key: key, value: sum;
);
);
barsTeam.exit().remove();
barsTeam = barsTeam
.enter()
.append("rect")
.attr("class", "barsTeam")
.attr("fill", d => z(d.key))
.attr("x", d => x2(d.key))
.attr("width", x1.bandwidth())
.merge(barsTeam);
barsTeam.transition().duration(durations)
.attr("y", d => y(d.value))
.attr("height", d => height - y(d.value));
percent.update = update;
function totals(data, team)
var width = 110 - (margin.left + margin.right),
height = 420 - (margin.top + margin.bottom);
var x = d3.scaleBand().rangeRound([0, width]).padding(0.1),
y = d3.scaleLinear().rangeRound([height, 0]);
var xAxis = d3.axisBottom(x),
yAxis = d3.axisLeft(y);
var svg = d3.select("#row1").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", "axis axis--x")
.attr("transform", "translate(0," + height + ")");
svg.append("g")
.attr("class", "axis axis--y");
update(data, team);
function update(data, team)
var sum = d3.sum(data, d => d3.sum([d.male, d.female]));
var sumTeam = d3.sum(data, function(d)
return d3.sum([d["Woman" + team], d["Man" + team]])
);
y.domain([0, sum]).nice();
x.domain(["Total"]);
svg.selectAll(".axis.axis--y")
.call(yAxis);
svg.selectAll(".axis.axis--x")
.call(xAxis);
var ttlBar = svg.selectAll(".ttlBar")
.data(["empty"]).enter()
.append("rect")
.attr("class", "ttlBar")
.attr("fill", "#ccc")
.attr("x", x(["Total"]))
.attr("y", y(sum))
.attr("width", x.bandwidth())
.attr("height", height - y(sum));
var ttlTeam = svg.selectAll(".ttlTeam")
.data(["empty"]);
ttlTeam = ttlTeam
.enter()
.append("rect")
.attr("class", "ttlTeam")
.attr("fill", "#999")
.attr("x", x(["Total"]))
.attr("width", x.bandwidth())
.merge(ttlTeam);
ttlTeam.transition().duration(durations)
.attr("height", height - y(sumTeam))
.attr("y", y(sumTeam))
totals.update = update
body
font: 12px arial;
margin: auto;
width: 850px;
padding-top:25px;
select
border: 1px solid #fff;
border-bottom: 1px solid #ccc;
cursor: pointer;
.M fill: steelblue;
.W fill: orange;
.hide path
display: none;
.hide .tick:not(:first-of-type) line
opacity: .25;
<meta charset="utf-8">
<script src="https://d3js.org/d3.v5.min.js"></script>
<div style="margin-left: 25px;">
<b>Choose Team:</b>
<select class="select1">
<option value="_one">Team 1</option>
<option value="_two">Team 2</option>
<option value="_three">Team 3</option>
</select>
</div>
<div id="row1"></div>
javascript d3.js
edited May 24 at 13:41
asked May 24 at 13:18
Robert Andersson
1667
1667
add a comment |Â
add a comment |Â
1 Answer
1
active
oldest
votes
up vote
1
down vote
accepted
My answer here will focus on refactoring the row1
function. Right now that's an awkward function, with some unnecessary logic.
Let's examine the it:
function row1(data)
var check = false;
update(check);
function update(check)
var team = d3.selectAll(".select1").property("value")
data.forEach(function(d, i, columns)
d.male = +(d.male);
d.female = +(d.female);
d["Woman" + team] = +d["Woman" + team];
d["Man" + team] = +d["Man" + team];
return d;
)
if (check)
pyramid.update(data, team)
percent.update(data, team)
totals.update(data, team)
else
pyramid(data, team);
percent(data, team);
totals(data, team);
d3.selectAll(".select1").on("change", function()
check = true;
durations = 750;
update(check);
)
In the linked Plunker, which uses a real CSV, you get the result of the promise and pass it to the row1
function. So far, so good.
However, inside that function, you're calling another function, named update
, which gets the selected value of a <select>
element. Then, based on that option, you're coercing only some values of the data array to numbers, but keeping the whole structure of the data array (you're not filtering or anything like that).
The forEach
inside update
is also strange: you don't need to return the object row. Also, the third argument, columns
, is probably a reminiscent of the third parameter in the d3.csv
: in a forEach
method the third argument is not the array of headers.
Continuing, you're using a check
variable (which is always true
after the user changes the select) to choose what functions to call, and finally setting a listener to the select that never listens to the value of the select! It just calls update
again...
This is my proposition to simplify this row1
function:
function row1(data)
data.forEach(function(d)
for (var key in d)
if (key !== "AgeRange")
d[key] = +d[key]
);
pyramid(data, "_one");
percent(data, "_two");
totals(data, "_three");
d3.selectAll(".select1").on("change", function()
durations = 750;
pyramid.update(data, this.value)
percent.update(data, this.value)
totals.update(data, this.value)
);
These are the changes:
- The
forEach
coerces all the values, except forAgeRange
; - You call the drawing functions with the data and the teams;
- Since you know what values you're using for the first time, I just hardcoded them. If you don't like that just use another selection to get the selected option (again, hardcoded!)
- You use the listener to get the value of the select with
this.value
, and call the update functions.
Also, if you put the drawing functions inside row1
, you don't even need to pass the data
to them (since the data never changes).
Here is the updated Plunker: https://plnkr.co/edit/UzFnmxC80ThA5VeGmFek?p=preview
add a comment |Â
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
1
down vote
accepted
My answer here will focus on refactoring the row1
function. Right now that's an awkward function, with some unnecessary logic.
Let's examine the it:
function row1(data)
var check = false;
update(check);
function update(check)
var team = d3.selectAll(".select1").property("value")
data.forEach(function(d, i, columns)
d.male = +(d.male);
d.female = +(d.female);
d["Woman" + team] = +d["Woman" + team];
d["Man" + team] = +d["Man" + team];
return d;
)
if (check)
pyramid.update(data, team)
percent.update(data, team)
totals.update(data, team)
else
pyramid(data, team);
percent(data, team);
totals(data, team);
d3.selectAll(".select1").on("change", function()
check = true;
durations = 750;
update(check);
)
In the linked Plunker, which uses a real CSV, you get the result of the promise and pass it to the row1
function. So far, so good.
However, inside that function, you're calling another function, named update
, which gets the selected value of a <select>
element. Then, based on that option, you're coercing only some values of the data array to numbers, but keeping the whole structure of the data array (you're not filtering or anything like that).
The forEach
inside update
is also strange: you don't need to return the object row. Also, the third argument, columns
, is probably a reminiscent of the third parameter in the d3.csv
: in a forEach
method the third argument is not the array of headers.
Continuing, you're using a check
variable (which is always true
after the user changes the select) to choose what functions to call, and finally setting a listener to the select that never listens to the value of the select! It just calls update
again...
This is my proposition to simplify this row1
function:
function row1(data)
data.forEach(function(d)
for (var key in d)
if (key !== "AgeRange")
d[key] = +d[key]
);
pyramid(data, "_one");
percent(data, "_two");
totals(data, "_three");
d3.selectAll(".select1").on("change", function()
durations = 750;
pyramid.update(data, this.value)
percent.update(data, this.value)
totals.update(data, this.value)
);
These are the changes:
- The
forEach
coerces all the values, except forAgeRange
; - You call the drawing functions with the data and the teams;
- Since you know what values you're using for the first time, I just hardcoded them. If you don't like that just use another selection to get the selected option (again, hardcoded!)
- You use the listener to get the value of the select with
this.value
, and call the update functions.
Also, if you put the drawing functions inside row1
, you don't even need to pass the data
to them (since the data never changes).
Here is the updated Plunker: https://plnkr.co/edit/UzFnmxC80ThA5VeGmFek?p=preview
add a comment |Â
up vote
1
down vote
accepted
My answer here will focus on refactoring the row1
function. Right now that's an awkward function, with some unnecessary logic.
Let's examine the it:
function row1(data)
var check = false;
update(check);
function update(check)
var team = d3.selectAll(".select1").property("value")
data.forEach(function(d, i, columns)
d.male = +(d.male);
d.female = +(d.female);
d["Woman" + team] = +d["Woman" + team];
d["Man" + team] = +d["Man" + team];
return d;
)
if (check)
pyramid.update(data, team)
percent.update(data, team)
totals.update(data, team)
else
pyramid(data, team);
percent(data, team);
totals(data, team);
d3.selectAll(".select1").on("change", function()
check = true;
durations = 750;
update(check);
)
In the linked Plunker, which uses a real CSV, you get the result of the promise and pass it to the row1
function. So far, so good.
However, inside that function, you're calling another function, named update
, which gets the selected value of a <select>
element. Then, based on that option, you're coercing only some values of the data array to numbers, but keeping the whole structure of the data array (you're not filtering or anything like that).
The forEach
inside update
is also strange: you don't need to return the object row. Also, the third argument, columns
, is probably a reminiscent of the third parameter in the d3.csv
: in a forEach
method the third argument is not the array of headers.
Continuing, you're using a check
variable (which is always true
after the user changes the select) to choose what functions to call, and finally setting a listener to the select that never listens to the value of the select! It just calls update
again...
This is my proposition to simplify this row1
function:
function row1(data)
data.forEach(function(d)
for (var key in d)
if (key !== "AgeRange")
d[key] = +d[key]
);
pyramid(data, "_one");
percent(data, "_two");
totals(data, "_three");
d3.selectAll(".select1").on("change", function()
durations = 750;
pyramid.update(data, this.value)
percent.update(data, this.value)
totals.update(data, this.value)
);
These are the changes:
- The
forEach
coerces all the values, except forAgeRange
; - You call the drawing functions with the data and the teams;
- Since you know what values you're using for the first time, I just hardcoded them. If you don't like that just use another selection to get the selected option (again, hardcoded!)
- You use the listener to get the value of the select with
this.value
, and call the update functions.
Also, if you put the drawing functions inside row1
, you don't even need to pass the data
to them (since the data never changes).
Here is the updated Plunker: https://plnkr.co/edit/UzFnmxC80ThA5VeGmFek?p=preview
add a comment |Â
up vote
1
down vote
accepted
up vote
1
down vote
accepted
My answer here will focus on refactoring the row1
function. Right now that's an awkward function, with some unnecessary logic.
Let's examine the it:
function row1(data)
var check = false;
update(check);
function update(check)
var team = d3.selectAll(".select1").property("value")
data.forEach(function(d, i, columns)
d.male = +(d.male);
d.female = +(d.female);
d["Woman" + team] = +d["Woman" + team];
d["Man" + team] = +d["Man" + team];
return d;
)
if (check)
pyramid.update(data, team)
percent.update(data, team)
totals.update(data, team)
else
pyramid(data, team);
percent(data, team);
totals(data, team);
d3.selectAll(".select1").on("change", function()
check = true;
durations = 750;
update(check);
)
In the linked Plunker, which uses a real CSV, you get the result of the promise and pass it to the row1
function. So far, so good.
However, inside that function, you're calling another function, named update
, which gets the selected value of a <select>
element. Then, based on that option, you're coercing only some values of the data array to numbers, but keeping the whole structure of the data array (you're not filtering or anything like that).
The forEach
inside update
is also strange: you don't need to return the object row. Also, the third argument, columns
, is probably a reminiscent of the third parameter in the d3.csv
: in a forEach
method the third argument is not the array of headers.
Continuing, you're using a check
variable (which is always true
after the user changes the select) to choose what functions to call, and finally setting a listener to the select that never listens to the value of the select! It just calls update
again...
This is my proposition to simplify this row1
function:
function row1(data)
data.forEach(function(d)
for (var key in d)
if (key !== "AgeRange")
d[key] = +d[key]
);
pyramid(data, "_one");
percent(data, "_two");
totals(data, "_three");
d3.selectAll(".select1").on("change", function()
durations = 750;
pyramid.update(data, this.value)
percent.update(data, this.value)
totals.update(data, this.value)
);
These are the changes:
- The
forEach
coerces all the values, except forAgeRange
; - You call the drawing functions with the data and the teams;
- Since you know what values you're using for the first time, I just hardcoded them. If you don't like that just use another selection to get the selected option (again, hardcoded!)
- You use the listener to get the value of the select with
this.value
, and call the update functions.
Also, if you put the drawing functions inside row1
, you don't even need to pass the data
to them (since the data never changes).
Here is the updated Plunker: https://plnkr.co/edit/UzFnmxC80ThA5VeGmFek?p=preview
My answer here will focus on refactoring the row1
function. Right now that's an awkward function, with some unnecessary logic.
Let's examine the it:
function row1(data)
var check = false;
update(check);
function update(check)
var team = d3.selectAll(".select1").property("value")
data.forEach(function(d, i, columns)
d.male = +(d.male);
d.female = +(d.female);
d["Woman" + team] = +d["Woman" + team];
d["Man" + team] = +d["Man" + team];
return d;
)
if (check)
pyramid.update(data, team)
percent.update(data, team)
totals.update(data, team)
else
pyramid(data, team);
percent(data, team);
totals(data, team);
d3.selectAll(".select1").on("change", function()
check = true;
durations = 750;
update(check);
)
In the linked Plunker, which uses a real CSV, you get the result of the promise and pass it to the row1
function. So far, so good.
However, inside that function, you're calling another function, named update
, which gets the selected value of a <select>
element. Then, based on that option, you're coercing only some values of the data array to numbers, but keeping the whole structure of the data array (you're not filtering or anything like that).
The forEach
inside update
is also strange: you don't need to return the object row. Also, the third argument, columns
, is probably a reminiscent of the third parameter in the d3.csv
: in a forEach
method the third argument is not the array of headers.
Continuing, you're using a check
variable (which is always true
after the user changes the select) to choose what functions to call, and finally setting a listener to the select that never listens to the value of the select! It just calls update
again...
This is my proposition to simplify this row1
function:
function row1(data)
data.forEach(function(d)
for (var key in d)
if (key !== "AgeRange")
d[key] = +d[key]
);
pyramid(data, "_one");
percent(data, "_two");
totals(data, "_three");
d3.selectAll(".select1").on("change", function()
durations = 750;
pyramid.update(data, this.value)
percent.update(data, this.value)
totals.update(data, this.value)
);
These are the changes:
- The
forEach
coerces all the values, except forAgeRange
; - You call the drawing functions with the data and the teams;
- Since you know what values you're using for the first time, I just hardcoded them. If you don't like that just use another selection to get the selected option (again, hardcoded!)
- You use the listener to get the value of the select with
this.value
, and call the update functions.
Also, if you put the drawing functions inside row1
, you don't even need to pass the data
to them (since the data never changes).
Here is the updated Plunker: https://plnkr.co/edit/UzFnmxC80ThA5VeGmFek?p=preview
edited May 25 at 3:32
answered May 24 at 14:13
Gerardo Furtado
1,1342420
1,1342420
add a comment |Â
add a comment |Â
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f195086%2fupdate-multiple-charts-simultaneously-in-d3-js%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password