This is a bar chart that initially displays a portion of the data in the csv file. It has a legend with one rectangle for each bar in the chart. When the rectangle is clicked, depending on the current state, the bar will be added or removed from the chart, by way of transitions.
xxxxxxxxxx
<html>
<head>
<meta charset='utf-8'>
<title>Bar Chart</title>
<style>
.bar {
fill: steelblue;
}
.legend rect {
stroke: steelblue;
stroke-width: 1;
fill: steelblue;
}
.axis text {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
</style>
</head>
<body>
<svg class="chart"></svg>
<script type="text/javascript" src="https://d3js.org/d3.v3.min.js"></script>
<script>
/* set everything up */
var margin = {top: 20,right: 80,bottom: 30,left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom,
xScale = d3.scale.ordinal().rangeRoundBands([0, width], .1),
yScale = d3.scale.linear().range([height, 0]);
var svg = d3.select(".chart")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom")
svg.append("g")
.attr("class", "x axis");
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left")
.ticks(10);
svg.append("g")
.attr("class", "y axis");
var initNumYearsToDisplay = 15,
yearsToRemove = [], // array holds the years not wanted in the display data
initialData = []; // array to hold the initial data (all the data)
filteredData = []; // array to hold the initial data (only data to be displayed)
/* get the data */
d3.csv("data.csv", type, function(error, data) {
if (error) throw error;
initialData = data;
filteredData = data;
// remove the extra years from the data so the display is good
yearsToRemove = getElementsInArray("year", data).slice(initNumYearsToDisplay);
yearsToRemove.forEach(function(e) { // and now filter out all unwanted YEARS
filteredData = filteredData.filter(function(item) { return item.year != e; });
});
update(filteredData); // call the update function and pass the data for the number of rectangles
});
/* display and update data */
function update(data) {
xScale.domain(data.map(function(d) { return d.year; })); // scale X inputs
yScale.domain([0, d3.max(data, function(d) { return d.value; })]); // scale Y inputs
svg.select(".x.axis") // display/update the X axis
.transition()
.duration(200)
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.select(".y.axis") // display/update the Y axis
.transition()
.duration(200)
.call(yAxis);
// bind the data to the rectangles and specify a key function
var bar = svg.selectAll(".bar")
.data(data, function(d) { return d.year; });
bar.enter().append("rect")
.attr("class", "bar")
.attr("y", yScale(0))
.attr("height", height - yScale(0))
.on("click", function(d, i) { return console.log(this); });
// UPDATE
bar.transition()
.duration(200)
.attr("x", function(d) { return xScale(d.year); })
.attr("width", xScale.rangeBand())
.attr("y", function(d) { return yScale(d.value); })
.attr("height", function(d) { return height - yScale(d.value); });
// REMOVE
bar.exit()
.transition()
.duration(200)
.attr("y", yScale(0))
.attr("height", height - yScale(0))
.style('fill-opacity', 1e-6)
.remove();
/* legend */
// append the g containers and bind the data to them
var legend = svg.selectAll(".legend")
.data(initialData.slice().reverse())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(20," + i * 22 + ")"; });
// append the rectangles
var legendRect = legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.attr("id", function(d, i) { return "myRect" + i; }) // add an id for the onclick
.attr("class", function(d, i) {
if (yearsToRemove.indexOf(d.year) < 0) { return "visible"; }
})
.attr("fill-opacity", function(d) {
if (yearsToRemove.indexOf(d.year) > -1) { return 0; }
});
// add the on click event
legendRect.on("click", function(d, i) {
var theRect = d3.select("#myRect" + i); // store the selection in a variable
var hasClass = theRect.classed("visible"); // store true or false if the visible class is in the selection
if (hasClass == true) { //Its visible!
// fade out the legend sqare fill
theRect.transition().duration(200).style('fill-opacity', 1e-6);
// remove the visible class from the selection
theRect.classed("visible", !theRect.classed("visible"));
// remove the year from the data array
filteredData = filteredData.filter(function(item) { return item.year != d.year; });
// update the display
update(filteredData);
} else { //Its NOT visible!
// fade in the legend sqare fill
theRect.transition().duration(200).style('fill-opacity', 1);
// add the visible class to the selection
theRect.classed("visible", !theRect.classed("visible"));
// add the year to the data array
filteredDataRemoved = filteredData.splice(filteredData.length, 0, d);
var filteredData2 = filteredData.sort(function(a, b) { return a.year - b.year; });
// update the display
update(filteredData2);
}
});
// append the text (year) to the group container
legend.append("text")
.attr("x", width + 5)
.attr("y", 9)
.attr("dy", ".35em")
.style("font-family", "sans-serif")
.style("text-anchor", "front")
.text(function(d) { return d.year; });
}
// get the unique elements from a d2 nested array
function getElementsInArray(elementType, data) {
return d3.nest()
.key(function(d) { return d[elementType]; })
.entries(data)
.map(function(d) { return d.key; });
}
// convert the value from string to number
function type(d) {
d.value = +d.value;
return d;
}
</script>
</body>
</html>
https://d3js.org/d3.v3.min.js