Chart.js Project — Interactive Sales Dashboard
In this tutorial, you will learn about Chart.js Project. We cover key concepts, practical examples, and best practices to help you master this topic.
This project walks through building a full-featured sales dashboard with Chart.js combining bar, line, doughnut, and radar charts with filtering and real-time data updates.
What You'll Learn
By the end of this project, you will architect a multi-chart dashboard layout, connect charts to shared data sources, implement date range filtering, add real-time data push, deploy with Responsive Design, and ensure Accessibility.
Dashboard Structure
var Dashboard = {
data: null,
filters: { startDate: null, endDate: null, region: 'all' },
charts: {},
init: function() {
this.loadData();
this.createCharts();
this.setupFilters();
},
loadData: function() {
fetch('/api/sales-data')
.then(function(res) { return res.json(); })
.then(function(data) {
Dashboard.data = data;
Dashboard.renderAll();
});
},
renderAll: function() {
var filtered = this.getFilteredData();
Object.keys(this.charts).forEach(function(key) {
Dashboard.charts[key].data = filtered;
Dashboard.charts[key].update();
});
}
};
Filter Handler
function setupFilters() {
d3.select('#date-range').on('change', function() {
Dashboard.filters.startDate = this.value.split(',')[0];
Dashboard.filters.endDate = this.value.split(',')[1];
Dashboard.renderAll();
});
d3.select('#region-select').on('change', function() {
Dashboard.filters.region = this.value;
Dashboard.renderAll();
});
}
Common Mistakes
1. Chart Instances Not Saved
Each chart must be saved to a variable for updates. Creating charts without references makes them impossible to update.
2. Data Transformation per Chart
Transform data once in a central place. Each chart should not re-parse or re-filter independently.
3. Not Destroying Charts on Page Leave
Leaving a page with active charts causes memory leaks. Call chart.destroy() in the page lifecycle.
4. Overcomplicating the Layout
Start with a simple grid. Add charts incrementally. Test each chart before adding the next.
5. Ignoring Error States
API calls can fail. Show error messages and retry buttons instead of blank spaces.
Practice Questions
Q1: How do you organize multiple charts in one page? A: Use a chart manager object that holds references to all chart instances and provides a central update function.
Q2: How do you share data between charts? A: Store data in a central state. Each chart reads from the state and updates when the state changes.
Q3: What is the best way to handle filter changes? A: Re-compute filtered data from the central state, then call chart.update('none') on each chart.
Q4: How do you ensure responsive layout? A: Use CSS Grid for layout, set responsive: true on each chart, and use container queries for breakpoints.
Q5: How do you add real-time updates? A: Use chartjs-plugin-streaming for the line chart and call chart.update('none') when new data arrives.
Challenge: Add export functionality: a button that downloads the entire dashboard as a PNG image using html2canvas.
FAQ
Try It Yourself
Build a mini dashboard with bar, line, and doughnut charts.
<!DOCTYPE html>
<html>
<head>
<title>Sales Dashboard</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
body { font-family: sans-serif; padding: 20px; background: #f5f5f5; }
.dashboard { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; max-width: 900px; margin: 0 auto; }
.card { background: white; border-radius: 8px; padding: 16px; box-shadow: 0 2px 8px rgba(0,0,0,0.08); }
.card h3 { margin: 0 0 8px; font-size: 13px; color: #666; text-transform: uppercase; }
.card.full { grid-column: 1 / -1; }
canvas { width: 100% !important; height: auto !important; }
#filters { margin-bottom: 12px; }
#filters select { padding: 6px 10px; border-radius: 6px; border: 1px solid #ddd; }
</style>
</head>
<body>
<div class="dashboard">
<div class="card full"><h3>Dashboard</h3><div id="filters"><select id="year-select"><option value="2024">2024</option><option value="2025" selected>2025</option></select></div></div>
<div class="card"><h3>Revenue</h3><canvas id="barChart"></canvas></div>
<div class="card"><h3>Trend</h3><canvas id="lineChart"></canvas></div>
<div class="card full"><h3>Distribution</h3><canvas id="doughnutChart" style="max-width:300px;margin:0 auto;"></canvas></div>
</div>
<script>
var revenueData = [30, 45, 40, 60, 55, 70, 65, 80, 75, 90, 85, 100];
var trendData = [20, 28, 35, 42, 40, 55, 50, 62, 58, 70, 68, 78];
var distData = [35, 25, 20, 15, 5];
var barChart = new Chart(document.getElementById('barChart'), { type: 'bar', data: { labels: ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'], datasets: [{ label: 'Revenue', data: revenueData, backgroundColor: '#4ecdc4' }] }, options: { responsive: true, maintainAspectRatio: true, plugins: { legend: { display: false } } } });
var lineChart = new Chart(document.getElementById('lineChart'), { type: 'line', data: { labels: ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'], datasets: [{ label: 'Trend', data: trendData, borderColor: '#ff6b35', fill: false, tension: 0.4 }] }, options: { responsive: true, maintainAspectRatio: true, plugins: { legend: { display: false } } } });
var doughnutChart = new Chart(document.getElementById('doughnutChart'), { type: 'doughnut', data: { labels: ['North America', 'Europe', 'Asia', 'South America', 'Africa'], datasets: [{ data: distData, backgroundColor: ['#ff6b35', '#4ecdc4', '#2a9d8f', '#e8a87c', '#95e1d3'] }] }, options: { responsive: true, maintainAspectRatio: true } });
</script>
</body>
</html>
What's Next
Explore Leaflet.js for interactive maps.
Getting Started — Interactive maps with Leaflet.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro