Formatting Currency And Split

Section overview

We will now improve our tip calculator by formatting currency values to look professional, and making the split counter display proper grammar (1 person vs 2 people).

You'll learn how to create reusable helper functions, format numbers with decimals, and use JavaScript's built-in internationalization API for currency formatting.

The formatting problem

Currently, our calculator displays raw numbers that can look messy:

  • Values like 15.666666666667 instead of $15.67
  • No dollar sign to indicate currency
  • "1 people" instead of "1 person"

Let's fix these issues to make our calculator more user-friendly!

Creating a currency formatting function

We'll create a helper function that takes a number and returns a formatted currency string. Add this function above your update function:

function formatMoney(value) {
  value = Math.ceil(value * 100) / 100;
  value = value.toFixed(2);
  return '$ ' + value;
}

function update() {
  // existing code...
}
  • function formatMoney(value) creates a reusable function that accepts a value parameter.
  • This function will format any number as currency with a dollar sign and two decimal places.
  • We'll break down what each line does in the next sections.

Rounding up with Math.ceil

The first line rounds our value up to the nearest cent:

value = Math.ceil(value * 100) / 100;
  • value * 100 converts dollars to cents (e.g. 15.666 becomes 1566.6).
  • Math.ceil() rounds up to the nearest whole number (1566.6 becomes 1567).
  • / 100 converts back to dollars (1567 becomes 15.67).

For example:

  • 15.666 becomes 15.67
  • 15.671 becomes 15.68
  • 15.60 stays 15.60

Fixing decimal places with toFixed

The second line ensures we always show exactly two decimal places:

value = value.toFixed(2)
  • .toFixed(2) converts the number to a string with exactly 2 decimal places.
  • 15.6 becomes "15.60"
  • 15.666 becomes "15.67"
  • This gives us consistent formatting for all currency values.

Note that toFixed() returns a string, not a number. This is fine since we're displaying the value, not calculating with it.

Adding the dollar sign

The final line adds the currency symbol:

return '$ ' + value;
  • '$ ' is a string with a dollar sign and a space.
  • + value concatenates (joins) the dollar sign with the formatted number.
  • return sends this formatted string back to wherever the function was called.

For example, formatMoney(15.666) returns "$ 15.67".

Using the formatMoney function

Now let's use our new function to format all currency displays. Update your update function:

document.getElementById('tipPercent').innerHTML = tipPercent + '%';
document.getElementById('tipValue').innerHTML = formatMoney(tipValue);
document.getElementById('totalWithTip').innerHTML = formatMoney(bill + tipValue);

document.getElementById('splitValue').innerHTML = split;
document.getElementById('billEach').innerHTML = formatMoney(newBillEach);
document.getElementById('tipEach').innerHTML = formatMoney(tipEach);
  • We wrap each currency value with formatMoney().
  • The tip percentage doesn't need formatting since it's already a percentage.
  • Now all dollar amounts will display consistently with $ XX.XX format.

Creating a split formatting function

Let's create another helper function to properly display "1 person" vs "2 people". Add this above your update function:

function formatSplit(value) {
  if (value === '1') return value + ' person'
  return value + ' people'
}
  • function formatSplit(value) creates a function to format the split count.
  • if (value === '1') checks if the value is exactly '1' (as a string).
  • return value + ' person' returns "1 person" for singular.
  • return value + ' people' returns the value plus " people" for plural cases.

The value is a string because it comes directly from the range input's .value property.

Using the formatSplit function

Now update the split value display in your update function:

document.getElementById('splitValue').innerHTML = formatSplit(split);
  • This replaces the raw number with properly formatted text.

Testing your formatted output

Open your calculator in the browser and test:

  1. Enter a bill amount like 100.5
  2. Move the tip slider to 15%
  3. Adjust the split slider

You should now see:

  • All currency values formatted as $ XX.XX
  • Split displaying as "1 person" or "X people"
  • Calculations rounded up to the nearest cent

Much better!

Your complete code so far

Here's what your JavaScript should look like now:

function formatMoney(value) {
  value = Math.ceil(value * 100) / 100
  value = value.toFixed(2)
  return '$ ' + value
}

function formatSplit(value) {
  if (value === '1') return value + ' person'
  return value + ' people'
}

function update() {
  let bill = Number(document.getElementById('yourBill').value)
  let tipPercent = document.getElementById('tipInput').value
  let split = document.getElementById('splitInput').value
  let tipValue = bill * (tipPercent / 100)
  let tipEach = tipValue / split
  let newBillEach = (bill + tipValue) / split

  document.getElementById('tipPercent').innerHTML = tipPercent + '%'
  document.getElementById('tipValue').innerHTML = formatMoney(tipValue)
  document.getElementById('totalWithTip').innerHTML = formatMoney(
    bill + tipValue
  )
  document.getElementById('splitValue').innerHTML = formatSplit(split)
  document.getElementById('billEach').innerHTML = formatMoney(newBillEach)
  document.getElementById('tipEach').innerHTML = formatMoney(tipEach)
}

const container = document.getElementById('container')
container.addEventListener('input', update)

Understanding the limitations

While our formatMoney function works well, it's a simplified approach with some limitations:

  1. Currency symbol is hardcoded: It always shows $, which won't work for other currencies.
  2. No locale support: Different countries format currency differently (e.g. $1,000.00 vs 1.000,00 $).
  3. Manual rounding logic: We have to handle the rounding ourselves.

For a production application or international audience, there's a better way!

Introducing Intl.NumberFormat

JavaScript includes a built-in API called Intl.NumberFormat that provides professional currency formatting with internationalization support.

Let's create an improved version of our formatMoney function:

function formatMoney(value) {
  return new Intl.NumberFormat('en-US', {
    style: 'currency',
    currency: 'USD',
  }).format(value)
}
  • Intl.NumberFormat is a built-in JavaScript object for formatting numbers.
  • 'en-US' specifies the locale (United States English formatting rules).
  • style: 'currency' tells it to format as currency.
  • currency: 'USD' specifies US Dollars.
  • .format(value) applies the formatting to our value.

How Intl.NumberFormat improves formatting

This approach has several advantages:

  1. Automatic rounding: No need for Math.ceil and division by 100.
  2. Proper decimal places: Automatically shows 2 decimal places for USD.
  3. Correct symbol placement: Follows locale rules for symbol positioning.
  4. Thousands separators: Automatically adds commas (e.g. $1,234.56).
  5. International support: Easy to change locale and currency.

For example:

  • formatMoney(1234.5) returns "$1,234.50"
  • formatMoney(0.666) returns "$0.67"
  • formatMoney(15) returns "$15.00"

Comparing both approaches

Here's a comparison of the two methods:

Simple approach (good for learning):

function formatMoney(value) {
  value = Math.ceil(value * 100) / 100
  value = value.toFixed(2)
  return '$ ' + value
}

Professional approach (better for real projects):

function formatMoney(value) {
  return new Intl.NumberFormat('en-US', {
    style: 'currency',
    currency: 'USD',
  }).format(value)
}

Both work perfectly fine for our calculator! The first approach is easier to understand when learning, while the second is more powerful and follows best practices.

Using different currencies (optional)

If you want to experiment with Intl.NumberFormat, try changing the currency:

British Pounds:

function formatMoney(value) {
  return new Intl.NumberFormat('en-GB', {
    style: 'currency',
    currency: 'GBP',
  }).format(value)
}

Euros:

function formatMoney(value) {
  return new Intl.NumberFormat('de-DE', {
    style: 'currency',
    currency: 'EUR',
  }).format(value)
}

Japanese Yen:

function formatMoney(value) {
  return new Intl.NumberFormat('ja-JP', {
    style: 'currency',
    currency: 'JPY',
  }).format(value)
}

Notice how each locale formats currency differently—the API handles all these details automatically!

Final code

Here's the complete HTML with formatted currency and split display:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <link
      href="https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,300;0,700;1,400&display=swap"
      rel="stylesheet"
    />
    <link rel="stylesheet" href="styles.css" />
    <title>Quick Tip</title>
  </head>
  <body>
    <main id="container">
      <h1>Quick Tip</h1>

      <section>
        <div class="bill">
          <label for="yourBill">Bill</label>
          <input type="number" placeholder="Your bill" id="yourBill" />
        </div>
        <div>
          <div class="space-between">
            <label for="tipInput">Select tip</label
            ><span id="tipPercent"></span>
          </div>
          <input type="range" value="0" id="tipInput" class="range" />
        </div>
        <div class="space-between">
          <span>Tip</span>
          <span id="tipValue"></span>
        </div>
        <hr />
        <div class="space-between total">
          <span>Total</span>
          <span id="totalWithTip"></span>
        </div>
      </section>

      <section>
        <div>
          <div class="space-between">
            <label for="splitInput">Split</label>
            <span id="splitValue"></span>
          </div>
          <input
            type="range"
            min="1"
            max="10"
            value="1"
            id="splitInput"
            class="range"
          />
        </div>
        <div class="space-between">
          <span>Bill each</span>
          <span id="billEach"></span>
        </div>
        <div class="space-between">
          <span>Tip each</span>
          <span id="tipEach"></span>
        </div>
      </section>
    </main>
    <script>
      function formatMoney(value) {
        value = Math.ceil(value * 100) / 100
        value = value.toFixed(2)
        return '$ ' + value
      }

      function formatSplit(value) {
        if (value === '1') return value + ' person'
        return value + ' people'
      }

      function update() {
        let bill = Number(document.getElementById('yourBill').value)
        let tipPercent = document.getElementById('tipInput').value
        let split = document.getElementById('splitInput').value
        let tipValue = bill * (tipPercent / 100)
        let tipEach = tipValue / split
        let newBillEach = (bill + tipValue) / split

        document.getElementById('tipPercent').innerHTML = tipPercent + '%'
        document.getElementById('tipValue').innerHTML = formatMoney(tipValue)
        document.getElementById('totalWithTip').innerHTML = formatMoney(
          bill + tipValue
        )
        document.getElementById('splitValue').innerHTML = formatSplit(split)
        document.getElementById('billEach').innerHTML = formatMoney(newBillEach)
        document.getElementById('tipEach').innerHTML = formatMoney(tipEach)
      }
      const container = document.getElementById('container')
      container.addEventListener('input', update)
    </script>
  </body>
</html>

Summary

In this lesson, you learned how to:

  • Create reusable helper functions to format values.
  • Use Math.ceil() to round numbers up to avoid underpayment.
  • Use .toFixed() to control decimal places in number formatting.
  • Concatenate strings to add currency symbols.
  • Handle singular and plural text properly with conditional logic.
  • Use the Intl.NumberFormat API for professional currency formatting.
  • Understand the trade-offs between simple and advanced formatting approaches.
  • Format currency for different locales and currencies.

Your JavaScript tip calculator is now complete!

fireworks image