Interest calculator
document.addEventListener(‘DOMContentLoaded’, function() {
// DOM Elements
const calculator = document.querySelector(‘.interest-calculator’);
const principalInput = document.getElementById(‘principal’);
const rateInput = document.getElementById(‘rate’);
const timeInput = document.getElementById(‘time’);
const timeUnitSelect = document.getElementById(‘time-unit’);
const frequencySelect = document.getElementById(‘frequency’);
const calculateBtn = document.getElementById(‘calculate’);
const resetBtn = document.getElementById(‘reset’);
const resultsContainer = document.getElementById(‘results’);
const darkModeToggle = document.getElementById(‘darkModeToggle’);
const darkIcon = document.getElementById(‘darkIcon’);
const lightIcon = document.getElementById(‘lightIcon’);
// Chart variable
let growthChart = null;
// Initialize dark mode from localStorage or system preference
function initDarkMode() {
const isDark = localStorage.getItem(‘darkMode’) === ‘true’ ||
(!localStorage.getItem(‘darkMode’) && window.matchMedia(‘(prefers-color-scheme: dark)’).matches);
if (isDark) {
document.documentElement.classList.add(‘dark’);
darkIcon.classList.add(‘hidden’);
lightIcon.classList.remove(‘hidden’);
} else {
document.documentElement.classList.remove(‘dark’);
darkIcon.classList.remove(‘hidden’);
lightIcon.classList.add(‘hidden’);
}
}
// Toggle dark mode
function toggleDarkMode() {
const isDark = document.documentElement.classList.toggle(‘dark’);
localStorage.setItem(‘darkMode’, isDark);
if (isDark) {
darkIcon.classList.add(‘hidden’);
lightIcon.classList.remove(‘hidden’);
} else {
darkIcon.classList.remove(‘hidden’);
lightIcon.classList.add(‘hidden’);
}
// Re-render chart with new theme colors
if (growthChart) {
growthChart.destroy();
renderChart();
}
}
// Format currency
function formatCurrency(amount) {
return new Intl.NumberFormat(‘en-US’, {
style: ‘currency’,
currency: ‘USD’,
minimumFractionDigits: 2,
maximumFractionDigits: 2
}).format(amount);
}
// Calculate interest
function calculateInterest() {
// Get input values
const principal = parseFloat(principalInput.value);
const rate = parseFloat(rateInput.value) / 100;
const time = parseFloat(timeInput.value);
const timeUnit = timeUnitSelect.value;
const frequency = parseInt(frequencySelect.value);
// Validate inputs
if (isNaN(principal) || principal <= 0) {
alert('Please enter a valid principal amount');
return;
}
if (isNaN(rate) || rate <= 0) {
alert('Please enter a valid interest rate');
return;
}
if (isNaN(time) || time <= 0) {
alert('Please enter a valid time period');
return;
}
// Convert time to years
const timeInYears = timeUnit === 'months' ? time / 12 : time;
// Calculate results
let totalInterest, finalAmount, breakdown = [];
if (frequency === 0) {
// Simple interest
totalInterest = principal * rate * timeInYears;
finalAmount = principal + totalInterest;
// Simple breakdown (start and end)
breakdown.push({ period: 'Start', interest: 0, balance: principal });
breakdown.push({ period: 'End', interest: totalInterest, balance: finalAmount });
} else {
// Compound interest
const periods = Math.round(frequency * timeInYears);
const periodicRate = rate / frequency;
finalAmount = principal * Math.pow(1 + periodicRate, periods);
totalInterest = finalAmount - principal;
// Generate detailed breakdown
let balance = principal;
const breakdownPoints = Math.min(12, periods); // Show max 12 points
for (let i = 0; i <= periods; i++) {
// Only include specific points to keep table manageable
if (i === 0 || i === periods || (periods > breakdownPoints && i % Math.floor(periods / breakdownPoints) === 0)) {
const interest = i === 0 ? 0 : balance * periodicRate;
if (i > 0) balance += interest;
breakdown.push({
period: i === 0 ? ‘Start’ :
i === periods ? ‘Final’ :
`Year ${(i / frequency).toFixed(1)}`,
interest: interest,
balance: balance
});
} else if (i > 0) {
balance += balance * periodicRate;
}
}
}
// Update results display
document.getElementById(‘result-principal’).textContent = formatCurrency(principal);
document.getElementById(‘result-interest’).textContent = formatCurrency(totalInterest);
document.getElementById(‘result-amount’).textContent = formatCurrency(finalAmount);
// Update breakdown table
const breakdownBody = document.getElementById(‘breakdown-body’);
breakdownBody.innerHTML = ”;
breakdown.forEach(item => {
const row = document.createElement(‘tr’);
row.className = ‘hover:bg-gray-50 dark:hover:bg-gray-500’;
const periodCell = document.createElement(‘td’);
periodCell.className = ‘px-4 py-3 whitespace-nowrap text-sm text-gray-800 dark:text-gray-200’;
periodCell.textContent = item.period;
row.appendChild(periodCell);
const interestCell = document.createElement(‘td’);
interestCell.className = ‘px-4 py-3 whitespace-nowrap text-sm text-right text-green-600 dark:text-green-400’;
interestCell.textContent = formatCurrency(item.interest);
row.appendChild(interestCell);
const balanceCell = document.createElement(‘td’);
balanceCell.className = ‘px-4 py-3 whitespace-nowrap text-sm text-right text-blue-600 dark:text-blue-400 font-medium’;
balanceCell.textContent = formatCurrency(item.balance);
row.appendChild(balanceCell);
breakdownBody.appendChild(row);
});
// Render chart
renderChart(breakdown, timeInYears);
// Show results
resultsContainer.classList.remove(‘hidden’);
}
// Render growth chart
function renderChart(breakdown = [], timeInYears = 1) {
const ctx = document.getElementById(‘growthChart’).getContext(‘2d’);
// Destroy previous chart if exists
if (growthChart) {
growthChart.destroy();
}
// Only render if we have breakdown data
if (breakdown.length === 0) return;
// Prepare chart data
const labels = breakdown.map(item => item.period);
const balanceData = breakdown.map(item => item.balance);
const interestData = breakdown.map(item => item.interest);
// Get theme colors
const isDark = document.documentElement.classList.contains(‘dark’);
const bgColor = isDark ? ‘rgba(17, 24, 39, 0.7)’ : ‘rgba(255, 255, 255, 0.7)’;
const textColor = isDark ? ‘rgba(229, 231, 235, 0.9)’ : ‘rgba(55, 65, 81, 0.9)’;
const gridColor = isDark ? ‘rgba(75, 85, 99, 0.5)’ : ‘rgba(209, 213, 219, 0.5)’;
growthChart = new Chart(ctx, {
type: ‘line’,
data: {
labels: labels,
datasets: [
{
label: ‘Account Balance’,
data: balanceData,
borderColor: ‘#3B82F6’,
backgroundColor: ‘rgba(59, 130, 246, 0.1)’,
borderWidth: 2,
tension: 0.1,
fill: true
},
{
label: ‘Interest Earned’,
data: interestData,
borderColor: ‘#10B981’,
backgroundColor: ‘rgba(16, 185, 129, 0.1)’,
borderWidth: 2,
tension: 0.1,
fill: true
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: ‘top’,
labels: {
color: textColor
}
},
tooltip: {
callbacks: {
label: function(context) {
let label = context.dataset.label || ”;
if (label) {
label += ‘: ‘;
}
if (context.parsed.y !== null) {
label += formatCurrency(context.parsed.y);
}
return label;
}
}
}
},
scales: {
x: {
grid: {
color: gridColor
},
ticks: {
color: textColor
}
},
y: {
grid: {
color: gridColor
},
ticks: {
color: textColor,
callback: function(value) {
return formatCurrency(value);
}
}
}
}
}
});
}
// Reset calculator
function resetCalculator() {
principalInput.value = ‘10000’;
rateInput.value = ‘5.0’;
timeInput.value = ‘5’;
timeUnitSelect.value = ‘years’;
frequencySelect.value = ’12’;
resultsContainer.classList.add(‘hidden’);
if (growthChart) {
growthChart.destroy();
growthChart = null;
}
}
// Event Listeners
calculateBtn.addEventListener(‘click’, calculateInterest);
resetBtn.addEventListener(‘click’, resetCalculator);
darkModeToggle.addEventListener(‘click’, toggleDarkMode);
// Initialize
initDarkMode();
// Load Chart.js library dynamically
if (typeof Chart === ‘undefined’) {
const script = document.createElement(‘script’);
script.src = ‘https://cdn.jsdelivr.net/npm/chart.js’;
script.onload = function() {
// If inputs have values, calculate immediately
if (principalInput.value && rateInput.value && timeInput.value) {
calculateInterest();
}
};
document.head.appendChild(script);
} else {
// If Chart.js is already loaded
if (principalInput.value && rateInput.value && timeInput.value) {
calculateInterest();
}
}
});