D3.js (Data-Driven Documents) is a powerful JavaScript library that enables you to create dynamic and interactive graphs directly in the browser.
With D3, you can create force-directed graphs, line graphs, and scatter plots that respond to user input and data changes.
In this tutorial, you’ll learn:
- How to create different types of graphs with D3.js.
- How to bind data to SVG elements.
- Techniques for customizing and animating graphs.
- How to handle interactivity (tooltips and hover effects).
1. Setting Up the Environment
Before diving into graph creation, set up a simple HTML file that loads D3.js from a CDN.
HTML Template:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>D3.js Graphs</title> <script src="https://d3js.org/d3.v7.min.js"></script> <style> svg { background-color: #f9f9f9; border: 1px solid #ddd; } .node { fill: steelblue; stroke: #fff; stroke-width: 2px; } .link { stroke: #aaa; stroke-width: 2px; } .tooltip { position: absolute; background: white; padding: 5px; border: 1px solid #ccc; border-radius: 4px; opacity: 0; } </style> </head> <body> <h1>D3.js Graph Examples</h1> <svg width="800" height="500"></svg> </body> </html>
2. Force-Directed Graph (Network Graph)
A force-directed graph displays nodes connected by links, commonly used to visualize relationships or networks.
Data for Graph:
const graphData = { nodes: [ { id: "Node 1" }, { id: "Node 2" }, { id: "Node 3" }, { id: "Node 4" } ], links: [ { source: "Node 1", target: "Node 2" }, { source: "Node 2", target: "Node 3" }, { source: "Node 3", target: "Node 4" }, { source: "Node 1", target: "Node 4" } ] };
Drawing the Force-Directed Graph:
const svg = d3.select("svg"); const width = svg.attr("width"); const height = svg.attr("height"); const simulation = d3.forceSimulation(graphData.nodes) .force("link", d3.forceLink(graphData.links).id(d => d.id).distance(150)) .force("charge", d3.forceManyBody().strength(-300)) // Repulsion .force("center", d3.forceCenter(width / 2, height / 2)); // Draw Links const link = svg.selectAll("line") .data(graphData.links) .enter() .append("line") .attr("class", "link"); // Draw Nodes const node = svg.selectAll("circle") .data(graphData.nodes) .enter() .append("circle") .attr("class", "node") .attr("r", 20) .call(drag(simulation)); // Enable drag behavior // Tooltip const tooltip = d3.select("body").append("div") .attr("class", "tooltip"); // Node Labels node.append("title") .text(d => d.id); // Simulate the Force simulation.on("tick", () => { link.attr("x1", d => d.source.x) .attr("y1", d => d.source.y) .attr("x2", d => d.target.x) .attr("y2", d => d.target.y); node.attr("cx", d => d.x) .attr("cy", d => d.y); }); // Drag Behavior function drag(simulation) { return d3.drag() .on("start", (event, d) => { if (!event.active) simulation.alphaTarget(0.3).restart(); d.fx = d.x; d.fy = d.y; }) .on("drag", (event, d) => { d.fx = event.x; d.fy = event.y; }) .on("end", (event, d) => { if (!event.active) simulation.alphaTarget(0); d.fx = null; d.fy = null; }); }
Explanation:
- Nodes and Links: Circles represent nodes, while lines represent links connecting them.
- Force Simulation: forceSimulation applies forces to nodes and links, creating a dynamic, interactive graph.
- Drag Behavior: Nodes can be dragged, and the graph automatically adjusts.
- Labels and Tooltips: Node labels appear on hover.
3. Line Graph (Time-Series or Trends)
Line graphs are ideal for visualizing data trends over time.
Sample Data (Time Series):
const lineData = [ { date: "2024-01-01", value: 30 }, { date: "2024-01-02", value: 50 }, { date: "2024-01-03", value: 80 }, { date: "2024-01-04", value: 40 }, { date: "2024-01-05", value: 60 } ];
Drawing the Line Graph:
const parseTime = d3.timeParse("%Y-%m-%d"); lineData.forEach(d => d.date = parseTime(d.date)); const x = d3.scaleTime() .domain(d3.extent(lineData, d => d.date)) .range([50, 750]); const y = d3.scaleLinear() .domain([0, d3.max(lineData, d => d.value)]) .range([400, 50]); const line = d3.line() .x(d => x(d.date)) .y(d => y(d.value)) .curve(d3.curveCatmullRom); svg.append("path") .datum(lineData) .attr("class", "line") .attr("d", line); // X Axis svg.append("g") .attr("transform", "translate(0, 400)") .call(d3.axisBottom(x).ticks(5)); // Y Axis svg.append("g") .attr("transform", "translate(50, 0)") .call(d3.axisLeft(y).ticks(5));
Explanation:
- X and Y Axes: Represent time (x-axis) and data values (y-axis).
- Path: The line generator creates a smooth path from the data points.
4. Scatter Plot (Visualizing Relationships)
Scatter plots visualize relationships between two variables.
Scatter Plot Data:
const scatterData = [ { x: 5, y: 20 }, { x: 15, y: 90 }, { x: 25, y: 60 }, { x: 35, y: 30 } ];
Drawing the Scatter Plot:
svg.selectAll("circle") .data(scatterData) .enter() .append("circle") .attr("cx", d => d.x * 20) .attr("cy", d => 400 - d.y * 3) .attr("r", 8) .attr("fill", "orange");
Conclusion:
With D3.js, creating force-directed graphs, line charts, and scatter plots is straightforward. By mastering force simulations, axis scaling, and interactive elements, you can build visually stunning and informative data visualizations.