Updating the Chart
D3 can handle updating selections and scales without having to rewrite a majority of the code.
We'll cover the following...
In this lesson, we are going to update the chart based on the user’s selection. This is where things are going to get tricky. We have to decide what gets updated and what does not get updated.
async function draw() {// Dataconst dataset = await d3.json('/udata/1kL2GBJw8VB/data-4-1.json')const xAccessor = d => d.currently.humidityconst yAccessor = d => d.length// Dimensionslet dimensions = {width: 800,height: 400,margins: 50};dimensions.ctrWidth = dimensions.width - dimensions.margins * 2dimensions.ctrHeight = dimensions.height - dimensions.margins * 2// Draw Imageconst svg = d3.select('#chart').append("svg").attr("width", dimensions.width).attr("height", dimensions.height)const ctr = svg.append("g") // <g>.attr("transform",`translate(${dimensions.margins}, ${dimensions.margins})`)// Create Scaleconst xScale = d3.scaleLinear().domain(d3.extent(dataset, xAccessor)).range([0, dimensions.ctrWidth]).nice()const bin = d3.bin().domain(xScale.domain()).value(xAccessor).thresholds(10)const newDataset = bin(dataset)const padding = 1const yScale = d3.scaleLinear().domain([0, d3.max(newDataset, yAccessor)]).range([dimensions.ctrHeight, 0]).nice()// Draw Barsctr.selectAll('rect').data(newDataset).join('rect').attr('width', d => d3.max([0, xScale(d.x1) - xScale(d.x0) - padding])).attr("height", d => dimensions.ctrHeight - yScale(yAccessor(d))).attr('x', d => xScale(d.x0)).attr("y", d => yScale(yAccessor(d))).attr('fill', '#01c5c4')ctr.append('g').classed('bar-labels', true).selectAll('text').data(newDataset).join('text').attr('x', d => xScale(d.x0) + (xScale(d.x1) - xScale(d.x0)) / 2).attr('y', d => yScale(yAccessor(d)) - 10).text(yAccessor)// Draw Axisconst xAxis = d3.axisBottom(xScale)const xAxisGroup = ctr.append("g").style("transform", `translateY(${dimensions.ctrHeight}px)`)xAxisGroup.call(xAxis)function histogram(metric) {}d3.select("#metric").on('change', function (e) {e.preventDefault()histogram(this.value)})}draw()
For example, if we scroll to the top of the function, we are making a request for the file. We do not need to repeat this request every time we change metrics. We only need the data once.
There are other parts we will need to update, such as the scales. We are going to move the things that need to be updated into the histogram()
function. Code that does not need to be repeated will remain where it is been written.
Refactoring code
We will start from the top and work our way down. After performing a request for a file, we are creating some accessor functions. The accessor functions will need to change based on the current metric. They are functions that will tell D3 which properties it should use for drawing the chart. We will move the accessor functions to the histogram()
function.
function histogram(metric) {const xAccessor = d => d.currently[metric]const yAccessor = d => d.length}
The accessor functions will need to be updated. Specifically, the xAccessor
function will need to be told which metric to return. We have an argument in the histogram function called metric
. We can use this argument to ...