
In this web development tutorial we are goin to create a payroll web application using HTML CSS / Bootstrap 5 for styling and JavaScript for the logic. We are also going to store data to session & local storage from where we are going to extract it and use it to calculate the total worked hours for one entire month. So lets get started with Building a Payroll Web App using JavaScript.
Donation!

Buy me a Pizza
Hi, if you like my content and you wish to give back you can buy me a Pizza!
$7.00
Donation!

Buy me a Pizza
Hi, if you like my content and you wish to give back you can buy me a Pizza!
$7.00
Steps:
1 – Create the payroll table in HTML using Bootstrap 5
2 – Hide and display table element using javascript and animation with CSS
3 – Create the employee data base using JSON and “random Name generator”
4 – Populate table using async await by load JSON data and display it to the DOM using map and join method
5 – Calculate Maximum Wage and Minimum Wage using the js Math method
6 – Calculate Average Wage using the js reduce method
7 – Calculate the months pay forEach employee by adding validation using condition if else statement, forEach loop and keyUp eventListener
7.1 – Save Data to browser local storage and / or session storage
8 – Get the total amount of payout for a specific month using the JS map method, filter method, parseFloat and substring and display it to the DOM
Useful Links:
You can also copy the code for this project “Build a Payroll Web App using JavaScript” from below and just follow along if you wish
1 Payroll Web App HTML code:
Lets start out with a container for the app. Add one buttons. The will activate the tables content.
Next you will add the table itself and give it a id of “PayrollTable”. You will use the id later on in your JavaScript to grab on to the table.
The Table contains a table head, witch is signified with the <thead> element and a table body, witch is signified with the <tbody> element. The table head will contain the head of columns and the table body the content of each column .
<h2 class="display-2 text-center my-5">Employee Payroll</h2>
<div class="container-md text-center">
<!--? Button to activate the table -->
<button class="btn btn-lg btn-primary text-center" id="NewPayroll">
New Payroll
</button>
<!--? Payroll Table -->
<table
id="PayrollTable"
class="table table-striped table-hover text-info shadow"
>
<thead>
<tr class="text-dark">
<th scope="col">#</th>
<th scope="col">First Name</th>
<th scope="col">Last Name</th>
<th scope="col">Hourly Wage</th>
<th scope="col">Hours Worked</th>
<th scope="col">Monthly Pay</th>
</tr>
</thead>
<tbody id="Employees-table">
<!-- <tr>
<th scope="row">1</th>
<td>Mark</td>
<td>Otto</td>
<td>@mdo</td>
</tr>
<tr>
<th scope="row">2</th>
<td>Jacob</td>
<td>Thornton</td>
<td>@fat</td>
</tr>
<tr>
<th scope="row">3</th>
<td colspan="2">Larry the Bird</td>
<td>@twitter</td>
</tr> -->
</tbody>
<tfoot id="Summery" class="fw-bold text-dark">
<tr>
<th scope="row">Summery</th>
<td class="text-success">Max : <span id="Max-wage">$00</span></td>
<td class="text-danger">Min : <span id="Min-wage">$00</span></td>
<td class="text-primary">Avg : <span id="Avg-wage">$00</span></td>
<td><span id="Total-WH">0000 h</span></td>
<td><span id="Total-pay">$ 0000 </span></td>
</tr>
</tfoot>
</table>
</div>
Payroll Web App CSS code:
Create a css file and call it style.css or main.css, I will live the naming up to you. Just pls do not forget to link it up to your index.html file I always do.
As you can see there is not much css to be coded here. The most part of the apps styling is done using bootstrap.
The only interesting part here are the input::-webkit-outer-spin-button, input::-webkit-inner-spin-button
This will remove the little arrow up and down from the input tags in your code. This make the inputs far better ecstatically in my opinion.
/* hide Table */
#PayrollTable {
display: none;
animation: fade 0.3s ease;
}
@keyframes fade {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
Payroll Web App JSON data code:
The JSON file will surve as our won data base. Pls add this file either to a separate folder coaled “db”, short for data base, or just leave it outside. I will personally recommend adding the file to the db folder.
Add to the JSON file an array of objects, each object will have key: value pairs for “id”: “1”, “firstName”: “Laura”, “lastName”: “Storm”, “hw”: variable.
[
{
"id": "1",
"firstName": "Laura",
"lastName": "Storm",
"hw": 14.46
},
{
"id": "2",
"firstName": "Jacob",
"lastName": "Thornton",
"hw": 19.47
},
{
"id": "3",
"firstName": "Zanna",
"lastName": "Tatyana",
"hw": 17.31
},
{
"id": "4",
"firstName": "Fábia",
"lastName": "Linda",
"hw": 15.3
},
{
"id": "5",
"firstName": "Ganbold ",
"lastName": "Anand",
"hw": 23.25
},
{
"id": "6",
"firstName": "Grier",
"lastName": "Seneca",
"hw": 34.31
},
{
"id": "7",
"firstName": "Rosalie",
"lastName": "Kenji",
"hw": 17.18
},
{
"id": "8",
"firstName": "Aurelia",
"lastName": "Widald",
"hw": 21.32
},
{
"id": "9",
"firstName": "Motya",
"lastName": "Yehonatan",
"hw": 54.78
},
{
"id": "10",
"firstName": "Ihintza",
"lastName": "Hildefons",
"hw": 18.2
}
]
Payroll Web app JavaScript code:
Now here comes the exiting part. JavaScript all the way.
As always start by getting all the elements from the DOM. And as alleyways I thinking of redoing this app in React.js.
First add functionality to the button in other to show and hide the payroll table.
Next lets the our data form the database that is in the JSON file. As you can see I have stored my employees.json file in the data folder as recommended. The content need to be added asynconasly. Don’t forget that JavaScript uses syncronas code and that will stop us from updating the data.
// Get elements
//Todo: Hide Button and Display Table
const newPayrollBtn = document.getElementById("NewPayroll");
newPayrollBtn.addEventListener("click", () => {
const payrollTable = document.getElementById("PayrollTable");
// console.log(payrollTable);
payrollTable.style.display = "table";
newPayrollBtn.style.display = "none";
});
//Todo: Get Data from JSON file
//* Employee First & Last Name
//* Hourly Wage
let personalList = [];
const loadEmployees = async () => {
try {
const res = await fetch("data/employees.json");
personalList = await res.json();
// console.log(personalList);
displayEmployees(personalList);
} catch (err) {
console.error(err);
}
};
// todo:
const displayEmployees = (employee) => {
const employeesTable = employee
.map((employee) => {
return `
<tr>
<th scope="row" >${employee.id}</th>
<td>${employee.firstName}</td>
<td>${employee.lastName}</td>
<td>$${employee.hw}</td>
<td><input type="number" class="hours-worked" style="width:60px" min="0"/> h</td>
<td class='monthly-pay fw-bold' > </td>
</tr>
`;
})
.join("");
document.getElementById("Employees-table").innerHTML = employeesTable;
//! -----------------------------------------------------
monthlyPay();
// console.log(employee);
//todo: 5+6 Outputs Max, Min & Avg
//* Get Hourly Wage
const getEmployeesHW = employee.map((employee) => employee.hw);
// console.log(getEmployeesHW);
//* Maximum Wage
let maxHW = calcMaxWage(getEmployeesHW);
document.getElementById("Max-wage").innerText = "$" + maxHW;
//* Minimum Wage
let minHW = calcMinWage(getEmployeesHW);
document.getElementById("Min-wage").innerText = "$" + minHW;
//* Average Wage
// get total amount of hourly wages
const getTotalHW = (total, hw) => total + hw;
const getAvgHW = (arr) => arr.reduce(getTotalHW, 0) / arr.length;
// console.log(getAvgHW(getEmployeesHW));
let avgHW = getAvgHW(getEmployeesHW).toFixed(2);
document.getElementById("Avg-wage").innerText = "$" + avgHW;
};
loadEmployees();
function monthlyPay() {
const hoursWorked = document.querySelectorAll(".hours-worked");
// console.log(hoursWorked);
hoursWorked.forEach((workHour) => {
workHour.addEventListener("keyup", (e) => {
// console.log(e.target.value);
//! Condition Action
if (e.target.value === "" || e.target.value <= 0) {
return;
} else {
// console.log("moths pay");
if (e.key === "Enter") {
// console.log(e.target.value);
// console.log(e.target.parentElement.parentElement.children[3].innerText);
const hour = e.target.value;
const hourlyWage = Number(
e.target.parentElement.parentElement.children[3].innerText.substring(
1
)
);
// console.log(e.target.parentElement.parentElement.children[5]);
let monthlyPay = e.target.parentElement.parentElement.children[5];
const calcMonthlyPay = (hour * hourlyWage).toFixed(2);
monthlyPay.innerText = "$" + calcMonthlyPay;
//todo: Set to Local / Session storage
saveData(hour);
//* Get Total Payouts
getTotalPayouts();
}
}
});
});
}
// Todo: Input
//* Hours Worked
//todo: Output
//* Monthly Pay = Hourly Wage * Hours Worked
//* Maximum Wage
function calcMaxWage(arr) {
return Math.max(...arr);
}
//* Minimum Wage
function calcMinWage(arr) {
return Math.min(...arr);
}
//* Total
//todo: Set to Local / Session storage
function saveData(hour) {
let hours;
if (sessionStorage.getItem("hours") === null) {
hours = [];
} else {
hours = JSON.parse(sessionStorage.getItem("hours"));
}
hours.push(hour);
sessionStorage.setItem("hours", JSON.stringify(hours));
// console.log(hours);
const newHours = hours.map((hour) => parseInt(hour));
// console.log(newHours);
let totalHours = newHours.reduce(calcTotal, 0);
// console.log(totalHours);
document.getElementById("Total-WH").innerText = totalHours + " h";
}
// ? uFunc for calculating total amount!
const calcTotal = (total, num) => {
return total + num;
};
function getTotalPayouts() {
const allMonthlyPays = document.querySelectorAll(".monthly-pay");
let arrayOfPayouts = Array.from(allMonthlyPays);
// console.log(arrayOfPayouts);
let newPayout = arrayOfPayouts.map((payout) =>
parseFloat(payout.innerHTML.substring(1))
);
// console.log(newPayout);
// * Return array elements with values
newPayout = newPayout.filter((payout) => payout);
// console.log(newPayout);
let calculateTotalPay = newPayout.reduce(calcTotal, 0);
document.getElementById("Total-pay").innerText =
"$" + calculateTotalPay.toFixed(2);
}
View the full video tutorial
Become a web developer!
Learn HTML CSS & Sass JavaScript Bootstrap and more…
