Categories We Write About

Build an interactive calendar heatmap

Creating an interactive calendar heatmap is a great way to visualize data over time, highlighting intensity or frequency on specific dates. Below is a detailed guide to build an interactive calendar heatmap using JavaScript with D3.js—a powerful library for data visualization.


Step 1: Setup Basic HTML Structure

html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <title>Interactive Calendar Heatmap</title> <style> body { font-family: Arial, sans-serif; } .day { shape-rendering: crispEdges; cursor: pointer; } .month-label { font-size: 12px; fill: #666; } .tooltip { position: absolute; padding: 6px 12px; background: rgba(0, 0, 0, 0.7); color: #fff; border-radius: 4px; pointer-events: none; font-size: 14px; opacity: 0; transition: opacity 0.3s ease; } </style> </head> <body> <div id="calendar"></div> <div class="tooltip" id="tooltip"></div> <script src="https://d3js.org/d3.v7.min.js"></script> <script> // JavaScript code goes here </script> </body> </html>

Step 2: Prepare Data

For a calendar heatmap, we need date-value pairs. Here’s an example data format (e.g., representing activity counts per day):

js
const data = [ { date: '2025-01-01', value: 3 }, { date: '2025-01-02', value: 7 }, { date: '2025-01-15', value: 2 }, // Add more date-value entries spanning the year ];

You can generate or load this data dynamically. For demonstration, we’ll generate random data for the entire year.


Step 3: Render Calendar Heatmap with D3.js

js
const width = 900; const height = 150; const cellSize = 17; // size of each day square const year = 2025; const svg = d3.select('#calendar') .append('svg') .attr('width', width) .attr('height', height * 4) // 4 rows for quarters approx .attr('font-family', 'sans-serif') .attr('font-size', 10); const tooltip = d3.select('#tooltip'); const formatDate = d3.timeFormat('%Y-%m-%d'); const parseDate = d3.timeParse('%Y-%m-%d'); const formatDay = d => d.getDay(); // 0=Sun, 6=Sat const formatWeek = d3.timeFormat('%U'); // Week number of year // Generate dummy data for entire year const startDate = new Date(year, 0, 1); const endDate = new Date(year, 11, 31); const dates = d3.timeDays(startDate, d3.timeDay.offset(endDate, 1)); const dataMap = new Map( dates.map(d => [formatDate(d), Math.floor(Math.random() * 10)]) // random 0-9 values ); // Color scale const color = d3.scaleSequential() .domain([0, 9]) .interpolator(d3.interpolateYlGnBu); function drawCalendar(year, offsetY) { const yearStart = new Date(year, 0, 1); // Month labels svg.selectAll('.month-label') .data(d3.timeMonths(yearStart, new Date(year + 1, 0, 1))) .join('text') .attr('class', 'month-label') .attr('x', d => d3.timeWeek.count(d3.timeYear(d), d) * cellSize + 20) .attr('y', offsetY - 5) .text(d => d3.timeFormat('%b')(d)); // Days rects svg.selectAll(`.day-${year}`) .data(d3.timeDays(yearStart, new Date(year + 1, 0, 1))) .join('rect') .attr('class', 'day') .attr('width', cellSize) .attr('height', cellSize) .attr('x', d => d3.timeWeek.count(yearStart, d) * cellSize + 20) .attr('y', d => formatDay(d) * cellSize + offsetY) .attr('fill', d => { const val = dataMap.get(formatDate(d)); return val != null ? color(val) : '#eee'; }) .on('mouseover', (event, d) => { const val = dataMap.get(formatDate(d)) || 0; tooltip.style('opacity', 1) .html(`<strong>${formatDate(d)}</strong><br/>Value: ${val}`) .style('left', (event.pageX + 10) + 'px') .style('top', (event.pageY - 28) + 'px'); }) .on('mouseout', () => tooltip.style('opacity', 0)); } // Draw calendar with some vertical offset for clarity drawCalendar(year, 20);

Explanation

  • SVG setup: A large SVG area to hold the calendar.

  • Data generation: Random data for each day of the year.

  • Color scale: d3.interpolateYlGnBu maps data values to colors.

  • Day rectangles: Positioned by week number (x) and day of week (y).

  • Month labels: Display abbreviated month names above the weeks.

  • Tooltip: Shows date and value on hover, follows mouse cursor.


Step 4: Expand Functionality (Optional)

  • Add a legend to explain color scale.

  • Allow selecting a date range.

  • Load real data dynamically (e.g., via API).

  • Add clickable interaction to filter or show detailed data.

  • Support multiple years stacked vertically.


This code provides a fully interactive calendar heatmap with smooth hover tooltips. You can customize the color scale, data source, and layout to fit your needs. If you’d like, I can help generate the full working code snippet or enhance it with features like zooming or dynamic data loading.

Share This Page:

Enter your email below to join The Palos Publishing Company Email List

We respect your email privacy

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

Categories We Write About