Create A Restaurant Bill and Tip Calculator in JavaScript

Create A Restaurant Bill and Tip Calculator in JavaScript

In this tutorial, I will show you how to create a simple restaurant bill and tip calculator using vanilla JavaScript, HTML, and SASS. You can use this tutorial to build your own and help you figure out the total bill amount per person and even what each person will need to tip based on selectable percentages.

This is a quick and easy project that anyone learning JavaScript can do. It uses Bootstrap for styling and we create a custom vanilla JavaScript class to handle the integration and calculations. All of it is wrapped up nicely in a visually appealing package that you can convert to an app or just use on your website.

View This On YouTube

File Structure

index.html
/sass
     style.scss
/calculator.js
/css (generated by Sass)
   style.css
   style.min.css

Our HTML

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Tip Calculator</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
    <link href="/css/style.min.css" rel="stylesheet">
</head>

<body>
    <div class="container mt-5">
        <h1 class="text-center">Tip Calculator</h1>
        <br>
        <div class="row justify-content-center">
            <div class="col-md-6">
                <div class="card">
                    <div class="card-body">
                        <div class="input-group">
                            <span class="input-group-text">
                                <i class="bi bi-currency-dollar"></i>
                            </span>
                            <input type="number" class="form-control form-control-lg total" min="0" step="0.01"
                                placeholder="Total Check">
                        </div>
                        <div class="input-group">
                            <span class="input-group-text">
                                <i class="bi bi-people-fill"></i>
                            </span>
                            <input type="number" class="form-control form-control-lg people" min="1"
                                placeholder="Total # of Guests">
                        </div>
                        <div class="text-center">
                            <label class="form-label">Select Tip Percentage</label>
                            <div class="form-check">
                                <div class="radio-button-label">
                                    <input type="radio" name="percentage" id="percentage0" class="percentage" checked value="0">
                                    <label for="percentage0">0%</label>
                                </div>
                                <div class="radio-button-label">
                                    <input type="radio" name="percentage" id="percentage10" class="percentage" value="10">
                                    <label for="percentage10">10%</label>
                                </div>
                                <div class="radio-button-label">
                                    <input type="radio" name="percentage" id="percentage15" class="percentage" value="15">
                                    <label for="percentage15">15%</label>
                                </div>
                                <div class="radio-button-label">
                                    <input type="radio" name="percentage" id="percentage18" class="percentage" value="18">
                                    <label for="percentage18">18%</label>
                                </div>
                                <div class="radio-button-label">
                                    <input type="radio" name="percentage" id="percentage20" class="percentage" value="20">
                                    <label for="percentage20">20%</label>
                                </div>
                            </div>
                        </div>
                        <br>
                        <p class="text-center">
                            <button type="button" class="btn btn-primary btn-lg calculateBtn">Calculate Tips</button>
                        </p>
                    </div>
                </div>
                <br>
                <div class="result">
                    <div class="row">
                        <div class="col-md-6">
                            <div class="card">
                                <div class="card-body">
                                    <h3>Total<span>Per Person</span></h3>
                                    <span class="perperson">$0.00</span>
                                </div>
                            </div>
                        </div>
                        <div class="col-md-6">
                            <div class="card">
                                <div class="card-body">
                                    <h3>Tip<span>Per Person</span></h3>
                                    <span class="tipamount">$0.00</span>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
    <script src="/calculator.js"></script>
</body>

</html>

We are using Bootstrap to create the HTML layout. In this layout, we will have an input for the total check, an input for the total number of guests, and finally a radio button object for the percentages.

I use Bootstrap cards to display this form, but you do not need to rely on Bootstrap for this type of interface. I implemented it to make it easier to get started with.

The Calculator Class

class TipCalculator {
    constructor() {
        // Create elements for the calculator
        this.button = document.querySelector(".calculateBtn");
        this.totalElement = document.querySelector(".total");
        this.peopleElement = document.querySelector(".people");
        this.percentageElements = document.querySelectorAll(".percentage");
        this.resultElement = document.querySelector(".result");

        // Attach the event listener in the constructor
        this.button.addEventListener("click", () => {
            this.calculateTip();
        });
    }

    calculateTip() {
        // Get the values from the inputs, format as needed
        const total = parseFloat(this.totalElement.value);
        const people = parseInt(this.peopleElement.value);
        let percentage = 0;

        // Use a variable to track validation errors
        let invalid = 0;

        // Remove 'is-invalid' class before revalidation
        this.totalElement.classList.remove("is-invalid");
        this.peopleElement.classList.remove("is-invalid");

        // Validate total and people inputs
        if (isNaN(total) || total <= 0) {
            this.totalElement.classList.add("is-invalid");
            invalid++;
        }

        if (isNaN(people) || people <= 0) {
            this.peopleElement.classList.add("is-invalid");
            invalid++;
        }

        if (invalid > 0) {
            return;
        }

        // Find the checked percentage
        this.percentageElements.forEach((element) => {
            if (element.checked) {
                percentage = parseInt(element.value);
            }
        });

        // Calculate tip and total per person
        const tipAmount = (total * (percentage / 100)) / people;
        const totalPerPerson = total / people + tipAmount;

        // Update the result element
        this.resultElement.querySelector(
            ".tipamount"
        ).textContent = `$${tipAmount.toFixed(2)}`;
        this.resultElement.querySelector(
            ".perperson"
        ).textContent = `$${totalPerPerson.toFixed(2)}`;
    }
}

I added comments to the JS so you can understand what it is doing but this is the general gist of it:

  1. use a constructor to create class variables based on the HTML DOM

  2. create a function that parses the values (if they exist, if not, return nothing and show an error), then it grabs the selected percentage and does some simple math to run the calculations.

At the end of this file, you can add the following code or you can add it after the script call in the index.html file.

// Create an instance of the TipCalculator
const calculator = new TipCalculator();

This will call the class and run it. If you add it to the index.html file, make sure you wrap it in <script></script> tags.

With that, you should have a working calculator. To make it styles like mine, you can start on the CSS. Again, this is not required but it will make it look nicer and more functional.

SASS

For the form, I am using the Bootstrap icons, so make sure that is imported into your SASS file. It is not required but helps to be informational.

@import url("https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css");

The main customization you need to worry about is the radio buttons and making them look like buttons. This is easy to accomplish by hiding the radio and using the label as a button. You can do that with the following:

form-check {
    padding-left:0;
    .radio-button-label {
        display:inline-block;
        cursor:pointer;

        & input[type="radio"] {
            display:none;
            &+label {
                border:2px solid #38a4ef;
                background-color:#fff;
                padding:6px 10px;
                border-radius:5px;
                color:#38a4ef;
            }
            &:checked+label {
                background-color:#38a4ef;
                color:#fff;
            }
        }
    }
}

That is really all you need to make it work like my video or the image above. If you want it to look exactly like mine, you can add the entire SASS stylesheet code to your project.

@import url("https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css");

body {
    font-size:1.25rem;
    color:#fff;
    background-color:#38a4ef;

    .card {
        border:0px;
        box-shadow: 0 5px 5px 5px rgba(0,0,0,.10);
    }
    .form-check {
        padding-left:0;
        .radio-button-label {
            display:inline-block;
            cursor:pointer;

            & input[type="radio"] {
                display:none;
                &+label {
                    border:2px solid #38a4ef;
                    background-color:#fff;
                    padding:6px 10px;
                    border-radius:5px;
                    color:#38a4ef;
                }
                &:checked+label {
                    background-color:#38a4ef;
                    color:#fff;
                }
            }
        }
    }
    span.input-group-text {
        background-color: #38a4ef;
        color:#fff;
        font-weight:bold;
        padding:0 1rem;
        border:2px solid #38a4ef;
        height:50px;
    }

    .form-control {
        margin-bottom:1rem;
        border:2px solid #38a4ef;

        &::placeholder {
            opacity: .5;
        }
    }

    .btn-primary {
        background-color: #38a4ef;
        border-color:#38a4ef;

        &:hover {
            background-color:#3383c7;
            border-color:#3383c7;
        }
    }

    .result {
        .card {
            margin-bottom:1rem;
        }
        margin-top:1rem;
        text-align:center;
        h3 {
            font-size:1.5rem;
        }
        span {
            display:block;
            font-size:1rem;
            color:rgba(0,0,0,.5);
            &.perperson, &.tipamount {
                font-size: 2.5rem;
                font-weight: bold;
                color:#38a4ef
            }
        }
    }
}

Conclusion

A simple class to make your bill-splitting time easier and more efficient. You can make modifications to the calculations or percentages pretty easily to make this your own, but as of now, the project should work as intended. I hope this gives you ideas. Let me know what you think and if you have any questions.

Read more articles on DevDrawer