Skip to content

D3.js - Working Example

D3.js - Working Example: Animated Bar Chart

Section titled “D3.js - Working Example: Animated Bar Chart”

This chapter demonstrates how to combine several D3.js concepts to create an animated bar chart. We’ll use a sample dataset, load it, and then render a bar chart with mouseover interactions and entry animations.

First, create a population-data.csv file (if you don’t have it from a previous chapter):

year,population
2006,40
2008,45
2010,48
2012,51
2014,53
2016,57
2017,62

Now, let’s build the bar chart step by step in an HTML file.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>D3 Animated Bar Chart</title>
<script src="https://d3js.org/d3.v7.min.js"></script>
<style>
body { font-family: Arial, sans-serif; }
.bar {
fill: steelblue;
transition: fill 0.2s ease-out;
}
.bar:hover, .bar.highlight {
fill: orange;
}
.chart-title {
font-size: 18px;
font-weight: bold;
text-anchor: middle;
}
.axis-label {
font-size: 12px;
text-anchor: middle;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.axis text {
font-family: sans-serif;
font-size: 10px;
}
.tooltip {
position: absolute;
text-align: center;
padding: 8px;
font: 12px sans-serif;
background: lightsteelblue;
border: 0px;
border-radius: 8px;
pointer-events: none; /* crucial for mouse events on bars */
opacity: 0;
}
</style>
</head>
<body>
<div id="chart-container"></div>
<script>
// D3 code follows
</script>
</body>
</html>

Step 2: Setup SVG, Margins, and Dimensions

Section titled “Step 2: Setup SVG, Margins, and Dimensions”
const container = d3.select("#chart-container");
const margin = {top: 60, right: 30, bottom: 70, left: 70};
const width = 600 - margin.left - margin.right;
const height = 400 - margin.top - margin.bottom;
const svg = container.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})`);
// Tooltip div
const tooltip = d3.select("body").append("div")
.attr("class", "tooltip");

d3.scaleBand() for the X-axis (years, categorical) and d3.scaleLinear() for the Y-axis (population, quantitative).

const xScale = d3.scaleBand()
.range([0, width])
.padding(0.2); // Padding between bars
const yScale = d3.scaleLinear()
.range([height, 0]); // Y-axis is inverted (0 at top)
d3.csv("population-data.csv", d => {
// Convert data types
return {
year: d.year, // Keep year as string for scaleBand
population: +d.population // Convert population to number
};
}).then(data => {
// Check data
console.log(data);
// Update scale domains based on data
xScale.domain(data.map(d => d.year));
yScale.domain([0, d3.max(data, d => d.population)]);
// Call function to draw chart elements (defined next)
drawChart(data);
}).catch(error => {
console.error("Error loading or processing data:", error);
container.text("Failed to load data for the chart.");
});

Step 5: Draw Chart Elements (Axes and Bars)

Section titled “Step 5: Draw Chart Elements (Axes and Bars)”
function drawChart(data) {
// Add X-axis
svg.append("g")
.attr("class", "axis x-axis")
.attr("transform", `translate(0,${height})`)
.call(d3.axisBottom(xScale))
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", "rotate(-45)");
// Add Y-axis
svg.append("g")
.attr("class", "axis y-axis")
.call(d3.axisLeft(yScale));
// Add Bars with entry animation
svg.selectAll(".bar")
.data(data)
.join("rect")
.attr("class", "bar")
.attr("x", d => xScale(d.year))
.attr("width", xScale.bandwidth())
.attr("y", d => yScale(0)) // Start from y=0 for animation
.attr("height", 0) // Start with height=0 for animation
.on("mouseover", handleMouseOver)
.on("mouseout", handleMouseOut)
.transition()
.duration(800)
.ease(d3.easeBounceOut) // A fun easing function
.delay((d, i) => i * 100) // Stagger the animation
.attr("y", d => yScale(d.population))
.attr("height", d => height - yScale(d.population));
// Add Chart Title
svg.append("text")
.attr("class", "chart-title")
.attr("x", width / 2)
.attr("y", 0 - (margin.top / 2))
.text("Population Growth Over Years");
// Add X-axis Label
svg.append("text")
.attr("class", "axis-label")
.attr("x", width / 2)
.attr("y", height + margin.bottom * 0.8)
.text("Year");
// Add Y-axis Label
svg.append("text")
.attr("class", "axis-label")
.attr("transform", "rotate(-90)")
.attr("y", 0 - margin.left * 0.8)
.attr("x", 0 - (height / 2))
.text("Population (in millions, fictional)");
}

Step 6: Mouse Event Handlers for Tooltip and Highlight

Section titled “Step 6: Mouse Event Handlers for Tooltip and Highlight”
function handleMouseOver(event, d) {
d3.select(this).classed("highlight", true);
tooltip.transition()
.duration(200)
.style("opacity", .9);
tooltip.html(`Year: ${d.year}<br/>Population: ${d.population}M`)
.style("left", (event.pageX + 5) + "px")
.style("top", (event.pageY - 28) + "px");
}
function handleMouseOut(event, d) {
d3.select(this).classed("highlight", false);
tooltip.transition()
.duration(500)
.style("opacity", 0);
}

Place all the JavaScript code within the <script> tags in your HTML file. When you open it in a browser, you should see a bar chart. Bars will animate into place, and hovering over a bar will highlight it and show a tooltip with its data.

  • Modular Structure: Separating concerns like setup, data loading, and drawing.
  • Margins and SVG Grouping: Using the D3 margin convention for layout.
  • Scales: d3.scaleBand for categorical data on the X-axis, d3.scaleLinear for quantitative data on the Y-axis.
  • Axes: Generating axes with d3.axisBottom and d3.axisLeft.
  • Data Loading (d3.csv): Fetching and parsing CSV data asynchronously.
  • Data Join (selection.join): Creating bars based on data.
  • Transitions and Easings: Animating the bars on entry (.transition(), .duration(), .ease(), .delay()).
  • Event Handling: Using .on("mouseover", ...) and .on("mouseout", ...) for interactivity.
  • Tooltips: Creating a simple HTML div tooltip that follows the mouse.

This example provides a solid template. You can further enhance it by adding features like sorting, filtering, or updating the chart with new data.