This example shows the General Update Pattern, which is required to create things with D3.js that update with dynamic data.
Part of the video course: D3.js in Motion.
See also:
license: mit |
This example shows the General Update Pattern, which is required to create things with D3.js that update with dynamic data.
Part of the video course: D3.js in Motion.
See also:
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8"> | |
<meta name="viewport" content="width=device-width"> | |
<title>Shopping App Tester</title> | |
<script src="https://d3js.org/d3.v4.min.js"></script> | |
</head> | |
<body> | |
<svg width="960" height="500"></svg> | |
<script src="visualization.js"></script> | |
<script> | |
const props = { | |
width: 300, | |
height: 500, | |
data: [ | |
{ name: 'banana', price: 1 }, | |
{ name: 'eggs', price: 2 }, | |
{ name: 'peanut butter', price: 4 } | |
] | |
}; | |
const svg = d3.select('svg'); | |
const selection = svg.append('g') | |
.attr('transform', `translate(${+svg.attr('width') / 2 - props.width / 2})`); | |
visualization(selection, props); | |
// Test the problem: bars don't update when price changes. | |
setTimeout(() => { | |
props.data[1].price = 3; | |
visualization(selection, props); | |
}, 1000); | |
// Test the problem: bars don't update correctly after an item is removed. | |
setTimeout(() => { | |
props.data = [ | |
{ name: 'banana', price: 1 }, | |
{ name: 'eggs', price: 2 }, | |
]; | |
visualization(selection, props); | |
}, 2000); | |
</script> | |
</body> | |
</html> |
function visualization(selection, props){ | |
const { width, height, data } = props; | |
const yValue = d => d.name; | |
const xValue = d => d.price; | |
const yScale = d3.scaleBand() | |
.domain(data.map(yValue)) | |
.range([height, 0]); | |
const xScale = d3.scaleLinear() | |
.domain([0, d3.max(data, xValue)]) | |
.range([0, width]); | |
// Create the data join. | |
const bars = selection.selectAll('rect').data(data); | |
// Handle the ENTER case - when elements first get added. | |
const barsEnter = bars | |
.enter().append('rect') | |
.attr('x', 0); // The x values won't change, so we can set it on ENTER. | |
// Set the bar position and dimensions on ENTER and UPDATE. | |
barsEnter | |
.merge(bars) | |
.attr('y', d => yScale(yValue(d))) | |
.attr('width', d => xScale(xValue(d))) | |
.attr('height', yScale.bandwidth()); | |
// Remove bars for which there are no longer any corresponding data elements. | |
bars.exit().remove(); | |
} |