In this chart I'm calling d3.csv
everytime I update the data, which if I understand correctly is a bad thing to do.
In this answer on S.O the answerer explains that d3.csv
is asynchronous and the data should only be loaded in once.
I understand why that is, but what I'm wondering now is what is a better way of doing this and if the way that I've done it in the sample code is ever acceptable.
<html>
<head>
<meta charset ="utf-8">
<script src="//d3js.org/d3.v4.min.js"></script>
<style>
body {
margin:auto;
width:850px;
font:10px arial;
padding:25px;
color:#555;
}
select {
border: none;
border-bottom: 1px solid #ccc;
padding: 3px;
text-decoration: none;
font-size: 11px;
cursor: pointer;
}
select:focus {
outline:0;
}
.sortCheck {
float:right;
margin-top:-35px;
margin-right:190px;
}
#myCheckbox {
position:relative;
top:3px;
}
/* --- Chart Css --- */
.barEnter {
fill: steelblue;
opacity: .9;
}
.barEnter:hover {
opacity: 1;
}
.axis--x path {
display: none;
}
.grid--y path,
.grid--y text {
display: none;
}
.grid--y line {
opacity:.15;
}
</style>
</head>
<body onclick>
<h2 style="font-weight:lighter;">Mulitple datasets test</h2>
<!-- Selection Category -->
<b style="font-size:11px;">
Choose Category
</b>
<select id="category">
<option value=" 1">Category 1</option>
<option value=" 2">Category 2</option>
</select>
<!-- Selection New dataset -->
<span style="margin-left:30px;">
<b style="font-size:11px;">
Choose Year
</b>
<select id="year" onchange="toggle();">
<option value="data2017">2017</option>
<option value="data2016">2016</option>
</select>
</span>
<!-- Chart -->
<div id="chart"></div>
<!-- Sort dimension -->
<span class="sortCheck">Sort Values
<input type="checkbox" id="myCheckbox">
</span>
<script>
let Globalvar = {}; // Global access variables
Globalvar.csvShift = "data.csv"; // CSV init
let formatValue = d3.format(",.0f");
let margin = {top: 35, right: 145, bottom: 35, left: 45},
width = 700 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
let x = d3.scaleBand().rangeRound([0, width]).padding(0.1),
y = d3.scaleLinear().rangeRound([height, 0]);
let xAxis = d3.axisBottom(x),
yAxis = d3.axisLeft(y),
yGrid = d3.axisLeft(y).tickSize(-width);
let g = d3.select("#chart").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 + ")");
let sideTextX = width + 30,
sideTextY = -height + height;
Globalvar.durations = 0;
function afterLoad() {
Globalvar.durations = 750;
};
// Sum of value
g.append("text")
.attr("fill","#555")
.attr("font-weight","bold")
.attr("font-size",11)
.attr("x", sideTextX)
.attr("y", sideTextY)
.text("Accumulated value: ")
g.append("text").attr("id","totalValue")
.attr("fill","#555")
.attr("font-size",11)
.attr("x", sideTextX)
.attr("y", sideTextY + 20);
// Average value
g.append("text")
.attr("fill","#555")
.attr("font-weight","bold")
.attr("font-size",11)
.attr("x", sideTextX)
.attr("y", sideTextY + 40)
.text("Average value: ")
g.append("text").attr("id","avgValue")
.attr("fill","#555")
.attr("font-size",11)
.attr("x", sideTextX)
.attr("y", sideTextY + 60);
g.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")");
g.append("g")
.attr("class", "axis axis--y");
g.append("g")
.attr("class", "axis grid--y");
// event handlers
d3.select("#myCheckbox").on('change', update);
d3.select("#category").on('change', update);
update();
var file, catInt;
function update() {
file = Globalvar.csvShift
catInt = d3.select('#category').property('value');
d3.csv(file, type, function(error,data) {
if(error) throw error;
var sortIndex = data.map(function(d){ return d.month});
// Update domain
y.domain([0, d3.max(data, function(d) {
return d["Category" + catInt]; })
]).nice();
// Update axis
g.selectAll(".axis.axis--y").transition()
.duration(Globalvar.durations)
.call(yAxis);
g.selectAll(".axis.grid--y").transition()
.duration(Globalvar.durations)
.call(yGrid);
// Sums and averages
let sumOfAll = d3.sum(data, function(d) {
return d["Category" + catInt];
});
let avgValue = d3.sum(data, function(d) {
return d["Category" + catInt];
}) / data.length;
//sort data
data.sort( d3.select("#myCheckbox").property("checked")
? function(a, b) { return b["Category" + catInt] - a["Category" + catInt]; }
: function(a, b) { return sortIndex.indexOf(a.month) - sortIndex.indexOf(b.month);})
// set x domain
x.domain(data.map(function(d) { return d.month; }));
g.selectAll(".axis.axis--x").transition()
.duration(Globalvar.durations)
.call(xAxis);
// Update rectangles
let bars = g.selectAll(".barEnter")
.data(data, function(d){
return d.month;
});
bars = bars
.enter()
.append("rect")
.attr("class", "barEnter") // Enter data reference
.attr("width", x.bandwidth())
.merge(bars);
bars.transition()
.duration(Globalvar.durations)
.attr("height", function(d) {
return height - y(d["Category" + catInt]);
})
.attr("x", function(d) {
return x(d.month);
})
.attr("y", function(d) {
return y(d["Category" + catInt]);
});
bars.exit().remove();
// Update text on rectangles
let textUpdate = g.selectAll(".textEnter")
.data(data, function(d){
return d.month;
});
textUpdate = textUpdate.enter()
.append("text")
.style("text-shadow","1px 1px #777")
.attr("class", "textEnter") // Enter data reference
.attr("text-anchor","middle")
.attr("font-size",11)
.attr("fill","#fff")
.merge(textUpdate);
textUpdate.transition()
.duration(Globalvar.durations)
.attr("y", function(d) {
return y(d["Category" + catInt]) + 15;
})
// Update text value
.text( function(d) {
return d["Category" + catInt];
})
.attr("x", function(d) {
return x(d.month) + x.bandwidth()/2;
})
// Update sum and avg value
g.selectAll("#totalValue").transition()
.duration(Globalvar.durations)
.text(sumOfAll + " Category " + catInt)
g.selectAll("#avgValue").transition()
.duration(Globalvar.durations)
.text(formatValue(avgValue))
afterLoad()
});
}
// Initialize csv data
function type(d) {
d["Category" + catInt] = +d["Category" + catInt];
return d;
}
// Toggle csv data
function toggle() {
if (document.getElementById('year').value == 'data2017') {
Globalvar.csvShift = "data.csv" ;
} else if
(document.getElementById('year').value == 'data2016') {
Globalvar.csvShift = "data2.csv";
} update();
}
</script>
</body>
</html>