Bar Chart Example
A complete, annotated bar chart built with d3-blueprint. This example composes an AxisChart and a BarsChart via attach(). See Reusable Components for the full source of those building blocks.
Live Preview
Full Source
js
import { select } from 'd3-selection';
import { D3Blueprint } from 'd3-blueprint';
import { scaleBand, scaleLinear } from 'd3-scale';
import { max } from 'd3-array';
import { AxisChart } from './charts/AxisChart.js';
import { BarsChart } from './charts/BarsChart.js';
import { tooltipPlugin } from './plugins/Tooltip.js';
const MARGIN = { top: 20, right: 20, bottom: 30, left: 40 };
class BarChart extends D3Blueprint {
initialize() {
this.chart = this.base
.append('g')
.attr('transform', `translate(${MARGIN.left},${MARGIN.top})`);
// Attach reusable axis and bar components
this.attach('axes', AxisChart, this.chart);
this.attach('bars', BarsChart, this.chart.append('g').classed('bars', true));
const innerWidth = 600 - MARGIN.left - MARGIN.right;
const innerHeight = 400 - MARGIN.top - MARGIN.bottom;
this.tooltip = tooltipPlugin(this.chart);
}
preDraw(data) {
const innerWidth = 600 - MARGIN.left - MARGIN.right;
const innerHeight = 400 - MARGIN.top - MARGIN.bottom;
this.xScale = scaleBand()
.domain(data.map((d) => d.label))
.range([0, innerWidth])
.padding(0.1);
this.yScale = scaleLinear()
.domain([0, max(data, (d) => d.value) ?? 0])
.range([innerHeight, 0]);
// Configure attachments. They read these when they draw.
this.attached.axes.config({ xScale: this.xScale, yScale: this.yScale, innerWidth, innerHeight, duration: 750 });
this.attached.bars.config({ xScale: this.xScale, yScale: this.yScale, innerHeight, fill: 'steelblue', duration: 750 });
}
postDraw() {
const tooltip = this.tooltip;
const xScale = this.xScale;
const yScale = this.yScale;
this.attached.bars.base.selectAll('rect')
.on('mouseenter', function (event, d) {
select(this).attr('opacity', 0.8);
tooltip.show(xScale(d.label) + xScale.bandwidth(), yScale(d.value), `${d.label}: ${d.value}`);
})
.on('mouseleave', function () {
select(this).attr('opacity', 1);
tooltip.hide();
});
}
}Usage
js
import { select } from 'd3-selection';
const chart = new BarChart(
select('#chart').append('svg').attr('width', 600).attr('height', 400),
);
await chart.draw([
{ label: 'A', value: 30 },
{ label: 'B', value: 86 },
{ label: 'C', value: 168 },
{ label: 'D', value: 47 },
]);
// Update with new data. Bars animate to new positions.
await chart.draw([
{ label: 'A', value: 80 },
{ label: 'B', value: 50 },
{ label: 'C', value: 120 },
{ label: 'D', value: 200 },
{ label: 'E', value: 65 },
]);Key Takeaways
attach()wires reusable components into the parent's draw lifecycle, so no manual orchestration is needed.preDraw()computes scales and passes them to attachments viaconfig(). The attachments read these configs when they draw.- AxisChart handles all axis rendering (ticks, transitions, tick counts) from configured scales.
- BarsChart handles the enter/update/exit data-join for bars, reading scales and styling from config.
- The parent chart has zero layers. It only computes data and delegates rendering to attachments.
postDraw()attaches mouse event handlers for the tooltip. Since d3-blueprint layers only support lifecycle events (enter/update/exit), DOM mouse events are wired via D3's.on()inpostDraw().