Build an Income and Expense Tracker using JavaScript


An income and expense tracker in JavaScript is an application that helps users keep track of their financial transactions. It allows users to input and categorize their income and expenses, view their overall financial standing, and identify areas where they may need to adjust their spending or saving habits. Here's a brief overview of how such an application could be implemented using JavaScript:

  • Data storage: The application needs to store data about each transaction, such as the amount, date, category, and any additional notes. This can be done using an array of objects or a database, depending on the complexity of the application.
  • Input form: Users need a way to input their transactions. This can be done using a form that collects information about each transaction and adds it to the data storage. The form should include fields for the amount, date, category, and notes.
  • Category management: Users should be able to categorize their transactions into different categories, such as groceries, entertainment, or bills. The application should provide a way for users to manage their categories and create new ones as needed.
  • Reporting: The application should provide users with a way to view their overall financial standing, such as their total income and expenses, as well as breakdowns by category. This can be done using charts or tables that summarize the data in the storage.
  • User interface: The application needs a user interface that is easy to use and visually appealing. This can be achieved using HTML, CSS, and JavaScript to create a responsive and intuitive interface.
  • Security: Finally, the application should ensure that user data is kept secure and private. This can be done using encryption and other security measures to protect the data from unauthorized access.

Overall, an income and expense tracker in JavaScript is a useful tool for individuals or small businesses to manage their finances effectively. By providing a simple, easy-to-use interface and powerful reporting features, it can help users make informed decisions about their spending and saving habits.

This is a JavaScript code for an income and expense tracker application. Let me explain each part of the code in detail:

DOM Elements:

const balance = document.querySelector("#balance");
const inc_amt = document.querySelector("#inc-amt");
const exp_amt = document.querySelector("#exp-amt");
const trans = document.querySelector("#trans");
const form = document.querySelector("#form");
const description = document.querySelector("#desc");
const amount = document.querySelector("#amount");

These lines are used to get the reference of the HTML DOM elements that are used in this application.

Local Storage:

const localStorageTrans = JSON.parse(localStorage.getItem("trans"));
let transactions = localStorage.getItem("trans") !== null ? localStorageTrans : [];

This code initializes an array variable transactions by checking if there are any saved transactions in the local storage. If there are saved transactions, they are parsed from the local storage and stored in the transactions variable. Otherwise, an empty array is used.

Load Transaction Details:

function loadTransactionDetails(transaction) {
  const sign = transaction.amount < 0 ? "-" : "+";
  const item = document.createElement("li");
  item.classList.add(transaction.amount < 0 ? "exp" : "inc");
  item.innerHTML = `
    ${transaction.description}
    <span>${sign} ${Math.abs(transaction.amount)}</span>
    <button class="btn-del" onclick="removeTrans(${transaction.id})">x</button>
  `;
  trans.appendChild(item);
}

This function is used to create an HTML list item (li) element for each transaction and add it to the transactions list on the HTML page. The transaction amount is formatted as positive or negative depending on whether it is an income or expense transaction, and a delete button is included to remove the transaction from the list.

Remove Transaction:

function removeTrans(id) {
  if (confirm("Are you sure you want to delete Transcation?")) {
    transactions = transactions.filter((transaction) => transaction.id != id);
    config();
    updateLocalStorage();
  } else {
    return;
  }
}

This function is called when the delete button is clicked for a transaction. It removes the transaction with the given ID from the transactions array, updates the transactions list on the HTML page, and updates the transactions in the local storage.

Update Amount:

function updateAmount() {
  const amounts = transactions.map((transaction) => transaction.amount);
  const total = amounts.reduce((acc, item) => (acc += item), 0).toFixed(2);
  balance.innerHTML = `${total}`;

  const income = amounts
    .filter((item) => item > 0)
    .reduce((acc, item) => (acc += item), 0)
    .toFixed(2);
  inc_amt.innerHTML = `${income}`;

  const expense = amounts
    .filter((item) => item < 0)
    .reduce((acc, item) => (acc += item), 0)
    .toFixed(2);
  exp_amt.innerHTML = `${Math.abs(expense)}`;
}

This function is used to update the total balance, income, and expenses displayed on the HTML page. It calculates the total amount by adding up all the transaction amounts, and separates the income and expenses by filtering the transaction amounts based on their sign.

Configuration:

function config() {
  trans.innerHTML = "";
  transactions.forEach(loadTransactionDetails);
  updateAmount();
}

This function is used to configure the initial state of the application.

Source Code

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <link rel="stylesheet" href="css/style.css" />
  </head>
  <body>
    <div class="container">
      <div class="ledger">
        <h2>Income Expense Tracker</h2>
        <h4>Your Balance</h4>
        <h1 id="balance">0.00</h1>
        <div class="inc-exp-container">
          <div class="inc">
            <h4>Income</h4>
            <p id="inc-amt" class="amt plus">0.00</p>
          </div>
          <div class="exp">
            <h4>Expense</h4>
            <p id="exp-amt" class="amt minus">0.00</p>
          </div>
        </div>
        <form action="#" id="form">
          <div class="form-control">
            <label for="desc">Description</label>
            <input type="text" name="desc" id="desc" placeholder="Enter Description" />
          </div>

          <div class="form-control">
            <label for="amount">Amount</label>
            <input type="number" name="amount" id="amount" placeholder="Amount" />
          </div>
          <button class="btn" type="submit">Add Transaction</button>
        </form>
      </div>
      <div class="transaction">
        <h3>Transaction Details</h3>
        <ul class="trans" id="trans">
          <li class="exp">
            Insurance
            <span>-1500</span>
            <button class="btn-del">x</button>
          </li>
          <li class="inc">
            Salary
            <span>35000</span>
            <button class="btn-del">x</button>
          </li>
          <li class="exp">
            Food
            <span>-350</span>
            <button class="btn-del">x</button>
          </li>
        </ul>
      </div>
    </div>
    <script src="js/script.js"></script>
  </body>
</html>

css/style.css

@import url("https://fonts.googleapis.com/css2?family=Source+Sans+Pro:ital,wght@0,300;0,400;0,700;0,900;1,600&display=swap");
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  font-family: "Source Sans Pro", sans-serif;
}

body {
  width: 100vw;
  height: 100vh;
  background-color: #dfe6e9;
  display: grid;
  place-items: center;
}

.container {
  width: 800px;
  height: 600px;
  background-color: #fff;
  box-shadow: rgba(0, 0, 0, 0.15) 1.95px 1.95px 2.6px;
  display: flex;
  user-select: none;
}

.ledger {
  background-color: #2f3542;
  padding: 20px;
  flex: 1;
  color: white;
}
.ledger h2 {
  text-transform: uppercase;
  color: #feca57;
}
.ledger h2,
.ledger h4,
.ledger h1 {
  font-weight: 400;
  margin-bottom: 5px;
  padding: 5px;
}
.inc-exp-container {
  display: flex;
  padding: 5px;
  gap: 10px;
}
.inc-exp-container div {
  text-align: center;
  text-transform: uppercase;
  padding: 20px;
  font-size: 18px;
  flex: 1;
  border-radius: 2px;
}

.inc-exp-container div p {
  font-size: 22px;
}
.inc-exp-container div.inc {
  border: 3px solid #1dd1a1;
  color: #1dd1a1;
}
.inc-exp-container div.exp {
  border: 3px solid #ee5253;
  color: #ee5253;
}

#form {
  padding: 5px;
  margin-top: 20px;
}
.form-control {
  margin-top: 10px;
}

label {
  display: inline-block;
  margin: 10px 0;
  font-size: 18px;
  font-weight: 400;
}

input[type="text"],
input[type="number"] {
  border: 1px solid #dedede;
  display: block;
  width: 100%;
  font-size: 16px;
  padding: 10px;
  outline: none;
  border-radius: 2px;
}

.btn {
  background-color: #2e86de;
  color: white;
  display: block;
  width: 100%;
  margin-top: 25px;
  padding: 10px;
  font-size: 16px;
  border: 0;
  font-weight: 600;
  border-radius: 5px;
}
.transaction {
  flex: 1;
  padding: 20px;
  display: flex;
  flex-direction: column;
  overflow-y: scroll;
}

.transaction h3 {
  text-transform: uppercase;
  padding-bottom: 10px;
  border-bottom: 1px solid #f1f1f1;
  margin-bottom: 5px;
}

.trans {
  list-style-type: none;
}
.trans li {
  background-color: #fff;
  color: #333;
  box-shadow: rgba(0, 0, 0, 0.02) 0px 1px 3px 0px, rgba(27, 31, 35, 0.15) 0px 0px 0px 1px;
  padding: 10px;
  margin: 10px 0;
  position: relative;
  display: flex;
  justify-content: space-between;
  font-weight: 600;
  cursor: pointer;
}

.trans li.inc {
  border-left: 5px solid #1dd1a1;
}
.trans li.exp {
  border-left: 5px solid #ee5253;
}
.btn-del {
  position: absolute;
  top: -10px;
  right: -10px;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  font-weight: 600;
  color: #fff;
  background-color: #e74c3c;
  border: none;
  font-size: 18px;
  line-height: 20px;
  cursor: pointer;
  opacity: 0;
}

.trans li:hover .btn-del {
  opacity: 1;
}

::-webkit-scrollbar {
  width: 5px;
}
::-webkit-scrollbar-track {
  background-color: #f1f1f1;
}

::-webkit-scrollbar-thumb {
  background-color: #2e86de;
  border-radius: 8px;
}

js/script.js

const balance = document.querySelector("#balance");
const inc_amt = document.querySelector("#inc-amt");
const exp_amt = document.querySelector("#exp-amt");
const trans = document.querySelector("#trans");
const form = document.querySelector("#form");
const description = document.querySelector("#desc");
const amount = document.querySelector("#amount");
/*
const dummyData = [
  { id: 1, description: "Flower", amount: -20 },
  { id: 2, description: "Salary", amount: 35000 },
  { id: 3, description: "Book", amount: 10 },
  { id: 4, description: "Camera", amount: -150 },
  { id: 5, description: "Petrol", amount: -250 },
];

let transactions = dummyData;
*/

const localStorageTrans = JSON.parse(localStorage.getItem("trans"));
let transactions = localStorage.getItem("trans") !== null ? localStorageTrans : [];

function loadTransactionDetails(transaction) {
  const sign = transaction.amount < 0 ? "-" : "+";
  const item = document.createElement("li");
  item.classList.add(transaction.amount < 0 ? "exp" : "inc");
  item.innerHTML = `
    ${transaction.description}
    <span>${sign} ${Math.abs(transaction.amount)}</span>
    <button class="btn-del" onclick="removeTrans(${transaction.id})">x</button>
  `;
  trans.appendChild(item);
  //console.log(transaction);
}

function removeTrans(id) {
  if (confirm("Are you sure you want to delete Transcation?")) {
    transactions = transactions.filter((transaction) => transaction.id != id);
    config();
    updateLocalStorage();
  } else {
    return;
  }
}

function updateAmount() {
  const amounts = transactions.map((transaction) => transaction.amount);
  const total = amounts.reduce((acc, item) => (acc += item), 0).toFixed(2);
  balance.innerHTML = `${total}`;

  const income = amounts
    .filter((item) => item > 0)
    .reduce((acc, item) => (acc += item), 0)
    .toFixed(2);
  inc_amt.innerHTML = `${income}`;

  const expense = amounts
    .filter((item) => item < 0)
    .reduce((acc, item) => (acc += item), 0)
    .toFixed(2);
  exp_amt.innerHTML = `${Math.abs(expense)}`;
}
function config() {
  trans.innerHTML = "";
  transactions.forEach(loadTransactionDetails);
  updateAmount();
}

function addTransaction(e) {
  e.preventDefault();
  if (description.value.trim() == "" || amount.value.trim() == "") {
    alert("Please Enter Description and amount");
  } else {
    const transaction = {
      id: uniqueId(),
      description: description.value,
      amount: +amount.value,
    };
    transactions.push(transaction);
    loadTransactionDetails(transaction);
    description.value = "";
    amount.value = "";
    updateAmount();
    updateLocalStorage();
  }
}

function uniqueId() {
  return Math.floor(Math.random() * 10000000);
}

form.addEventListener("submit", addTransaction);

window.addEventListener("load", function () {
  config();
});

function updateLocalStorage() {
  localStorage.setItem("trans", JSON.stringify(transactions));
}

The code also defines a function loadTransactionDetails(transaction) that creates a list item element for a given transaction object and appends it to the transaction list. The function checks if the amount of the transaction is positive or negative and assigns a class accordingly. It also displays the transaction amount with the appropriate sign and includes a delete button that triggers the removeTrans() function when clicked.

The removeTrans(id) function removes a transaction from the transactions array by filtering out the transaction with the given id. It then calls the config() function to update the transaction list and amount totals, and also calls updateLocalStorage() to update the data in the browser's local storage.

The updateAmount() function calculates and updates the balance, income, and expense amounts based on the current state of the transactions array. It first maps the array to an array of transaction amounts, then uses the reduce() method to calculate the total, income, and expense amounts.

The config() function clears the transaction list element and calls

loadTransactionDetails() for each transaction in the transactions array. It then calls updateAmount() to update the amount totals.

The addTransaction(e) function is triggered when the form is submitted. It first checks if both the description and amount fields have a value, and if not, displays an alert message. Otherwise, it creates a new transaction object with a unique ID using the uniqueId() function, and adds it to the transactions array. It then calls loadTransactionDetails() for the new transaction, updates the amount totals using updateAmount(), clears the form fields, and calls updateLocalStorage() to update the data in the browser's local storage.

Finally, the code adds event listeners for the form submit and window load events, which call the addTransaction() and config() functions, respectively.

Output

Tab Income and Expense Tracker

Live Preview


List of Programs


JS Practical Questions & Answers


JS Practical Project