Reputation: 610
I have a stacked area chart built in d3.js and I want to update how the data is displayed on a button click-- showing absolute numbers, relative numbers, and displaying the data as a silhouette. I have worked out how to create these three formats with my data, but when I update the chart, it redraws it rather than updating the stacked areas. I'm not able to see what I'm doing wrong. I would greatly appreciate if someone could help!
My code is below, and can also be viewed in this Plunker: https://plnkr.co/edit/Y5zmRLnSJu0SALj3?open=lib%2Fscript.js
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font: 12px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: none;
shape-rendering: crispEdges;
}
.browser text {
text-anchor: end;
}
</style>
<body>
<script src="https://d3js.org/d3.v4.js"></script>
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"></script>
<div id="chartType">
<div class="form-check form-check-inline">
<input type="radio" name="ctype" class="chartTypeRadio" value="Number" checked>
<label class="charttype">Number</label>
<input type="radio" name="ctype" class="chartTypeRadio" value="Percent">
<label class="charttype">Percent</label>
<input type="radio" name="ctype" class="chartTypeRadio" value="Silhouette">
<label class="charttype">Silhouette</label>
</div>
</div>
<div id="chart">
</div>
</body>
<script>
var select2 = d3.selectAll(".chartTypeRadio")
.on("change", function () {
let type = $("input[type='radio'][name='ctype']:checked").val();
console.log('type', type)
chartType(type, 750)
})
var margin = { top: 50, right: 0, bottom: 30, left: 50 },
width = 740 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
var parseDate = d3.timeParse("%m/%d/%Y");
var formatSi = d3.format(".3s");
var formatNumber = d3.format(".1f"),
formatThousand = function (x) { return formatNumber(x / 1e3); };
var x = d3.scaleTime()
.range([0, width]);
var y = d3.scaleLinear()
.range([height, 0]);
var color = d3.scaleOrdinal()
.domain(["18 to 25", "26 to 34", "35 to 39", "40 to 44", "45 & Over"])
.range(["#EF4136", "#56423E", "#BEA6A1", "#00A0AA", "#006976"])
var xAxis = d3.axisBottom(x).ticks(5)
var yAxis = d3.axisLeft(y)
var area = d3.area()
.x(function (d) {
return x(d.data.date);
})
.y0(function (d) { return y(d[0]); })
.y1(function (d) { return y(d[1]); });
var stack = d3.stack()
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 + ')');
chartType('Number', 750)
function chartType(type, speed) {
console.log('type', type)
d3.csv('data.csv', function (error, data) {
color.domain(d3.keys(data[0]).filter(function (key) { return key !== 'date'; }));
var keys = data.columns.filter(function (key) { return key !== 'date'; })
data.forEach(function (d) {
d.date = parseDate(d.date);
});
var maxDateVal = d3.max(data, function (d) {
var vals = d3.keys(d).map(function (key) { return key !== 'date' ? d[key] : 0 });
return d3.sum(vals);
});
x.domain(d3.extent(data, function (d) { return d.date; }));
y.domain([0, maxDateVal])
if (type == 'Percent') {
y.domain([0, 1])
}
else if (type == 'Silhouette') {
y.domain([-maxDateVal / 2, maxDateVal / 2])
}
stack.keys(keys);
stack.order(d3.stackOrderNone);
stack.offset(d3.stackOffsetNone);
if (type == 'Percent') {
stack.offset(d3.stackOffsetExpand);
}
else if (type == 'Silhouette') {
stack.offset(d3.stackOffsetSilhouette)
}
var segments = svg.append('g').attr('class', 'segments')
var segment = segments.selectAll('path')
.data(stack(data))
segment.enter()
.append('path')
.attr('class', 'segment')
.merge(segment);
segments.selectAll('.segment')
.transition().duration(850).ease(d3.easeCubicInOut)
.attr('d', area)
.style('fill', function (d) { return color(d.key); });
segment.exit().remove();
svg.selectAll(".axis").remove();
svg.append('g')
.attr('class', 'x axis')
.attr('transform', 'translate(0,' + height + ')')
.call(xAxis);
svg.append('g')
.attr('class', 'y axis')
.call(yAxis);
var legend = svg.selectAll(".legend")
.data(stack(data))
.enter()
.append("g")
.attr("class", "legend")
.attr("transform", "translate(" + (0) + "," + -40 + ")");
legend.append("rect")
.attr("x", function (d, i) { return 100 * i; })
.attr("y", 0)
.attr("width", 10)
.attr("height", 10)
.style('fill', function (d) { return color(d.key); })
legend.append("text")
.attr("dx", "1.15em")
.attr("x", function (d, i) { return 100 * i; })
.attr("y", 10)
.text(function (d) {
return d.key
})
});
}
</script>
</body>
</html>
Upvotes: 1
Views: 401
Reputation: 7210
Your segments
group is recreated each time you choose another type of chart.
Replace:
var segments = svg.append('g').attr('class', 'segments')
with:
var segments = svg.select('g.segments');
if (!segments.node())
segments = svg.append('g').attr('class', 'segments')
See it working here.
Upvotes: 1