Mortgage loan calculator with JavaScript

bosspetta

Enrique

Posted on February 23, 2022

Mortgage loan calculator with JavaScript

First time here, first time writing about JavaScript, first time writing an article in English… Definitely this is the article of the first times in everything.

A few days ago, reading Josh Comeau's fantastic article «How To Learn Stuff Quickly», I discovered the «Learn in Public» method to help me to learn and understand all new concepts that I'm trying to add to my professional career. Part of this method, or an application example, is precisely to write about the new concepts that you are learning to help you to learn them, understand them and incorporate them into your skills. But… without further ado, let's get started.

As part of my training in JavaScript, I have been doing quite a few exercises. There are many websites dedicated to that. One of them is «frontendeval». For this tutorial, I have chosen the "Mortgage Calculator" exercise.

For me, the biggest difficulty was not in the JavaScript logic but in understanding how the mathematical formula that performs the calculation works, this is it:

P(r(1+r)^n/((1+r)^n)-1))

I spent days trying to figure it out; calculus was never my thing. But luckily, the Internet was always there to help, and this video "How to Calculate a Mortgage Payment" clarified all the concepts for me.

I now had the necessary tools to start developing my Mortgage Calculator, so let's start coding.

We'll need a piece of HTML for our mortgage form:

<main class="mortgage-form-wrapper">
  <header>
    <h1>Mortgage calculator 🧮</h1>
  </header>
  <form id="mortgage-form" action="" class="mortgage-form">
    <div class="mortgage-form--row">
      <label for="amount-input">Principal loan amount</label>
      <input type="number" name="amount-input" id="amount-input" min="50000" placeholder="Min 50000" required>
      <p class="mortgage-form--help">Min 50000</p>
    </div>
    <div class="mortgage-form--row">
      <label for="interest-rate-input">Interest rate</label>
      <input type="number" name="amount-input" id="interest-rate-input" min="1" max="20" placeholder="Min 1% max 20%" required>
      <p class="mortgage-form--help">Min 1% max 20%, without '%' symbol</p>
    </div>
    <div class="mortgage-form--row">
      <label for="length-of-loan-input">Length of loan</label>
      <input type="number" name="amount-input" id="length-of-loan-input" min="1" max="40" placeholder="Min 1 year, max 40 years" required>
      <p class="mortgage-form--help">Min 1 year, max 40 years</p>
    </div>
    <div class="mortgage-form--row mortgage-form--row__button-wrapper">
      <button type="button" id="calculate-btn">Calculate</button>
      <button type="reset" id="reset-btn" class="reset-btn">Reset</button>
    </div>
  </form>
  <p class="motgage-result"><span id="mortgage-final-result"></span></p>
</main>
Enter fullscreen mode Exit fullscreen mode

And maybe a bit of CSS (SCSS):

@import url('https://fonts.googleapis.com/css2?family=Lato:ital,wght@0,400;0,700;1,400;1,700&display=swap');

* { box-sizing: border-box; }

::placeholder {
  font-size: small;
  color: #aaa
}

html, body {
  margin: 0;
  padding: 0;
}

body {
  font-family: 'Lato', sans-serif;
  display: flex;
  flex-direction: column;
  height: 100vh;
  align-items: center;
  justify-content: center;
  background: transparent url('../img/background.jpg') center center no-repeat;
  background-size: cover;
  padding: 0 15px;
}

a {
  text-decoration: none;
  transition: all .3s ease;
  &:hover, &:focus { text-decoration: underline; }
}

.photo-credit {
  position: absolute;
  bottom: 15px;
  right: 15px;
  font-size: .725rem;
  a {
    color: white;
    opacity: .5;
    &:hover, &:focus { opacity: 1; }
  }
}

.mortgage-form-wrapper {
  background-color: white;
  padding: 30px 30px 0;
  box-shadow: 0 0 5px rgba(black,.25);
  min-width: 420px;
  @media screen and (max-width: 480px) {
    min-width: 0;
    width: 100%;
  }
  h1 { margin: 0 0 15px; }
  input {
    transition: all .5s ease;
    &[type="number"] {
      border: 1px solid #ddd;
      border-radius: 5px;
      width: 100%;
      font-size: small;
      padding: 11px;
      &:invalid {
        background-color: rgba(lightcoral,.075);
        border: 1px solid lightcoral;
        outline: none;
      }
      &:empty {
        background-color: white;
        border: 1px solid #ddd;
      }
      &:valid {
        background-color: rgba(lightseagreen,.075);
        border: 1px solid lightseagreen;
        outline: none;
      }
      &.error {
        background-color: rgba(lightcoral,.075);
        border: 1px solid lightcoral;
        outline: none;
        &:valid {
          background-color: rgba(lightseagreen,.075);
          border: 1px solid lightseagreen;
          outline: none;
        }
      }
    }
  }
  p { margin: 0 0 15px; }
  label { display: block; }
}

p.motgage-result {
  margin: 0 0 15px;
  .error-message {
    color: lightcoral;
    font-weight: 700;
  }
  .success-message {
    color: lightseagreen;
    font-weight: 700;
  }
}

.mortgage-form--help {
  font-size: small;
  font-style: italic;
  color: #a9a9a9;
  text-align: right;
}

.mortgage-form--row__button-wrapper {
  text-align: center;
  margin-bottom: 30px;
  display: flex;
  justify-content: center;
  button {
    border: none;
    background-color: lightcoral;
    padding: 12px 20px;
    color: white;
    font-weight: 700;
    text-transform: uppercase;
    cursor: pointer;
    transition: all 0.3s ease;
    outline: 2px solid lightcoral;
    margin: 0 7.5px;
    &:hover, &:focus {
      background-color: #666;
      outline: 2px solid #666;
    }
    &.form-success,
    &.disabled {
      background-color: #a9a9a9;
      outline: 2px solid #a9a9a9;
      cursor: not-allowed;
      &:hover, &:focus {
        background-color: #a9a9a9;
        outline: 2px solid #a9a9a9;
      }
    }
  }
}

.reset-btn { display: none; }
Enter fullscreen mode Exit fullscreen mode

I used this image for background, feel free to use whichever you want. At this point, your mortgage form should look something like this:

Image description

First, we are going to store our form inputs into JavaScript variables:

// The principal loan amount
const amountInput = document.getElementById('amount-input')
// The interest rate of our loan
const interestRateInput = document.getElementById('interest-rate-input')
// The length of our loan
const lengthOfLoanInput = document.getElementById('length-of-loan-input')
Enter fullscreen mode Exit fullscreen mode

Next, create a function for the calculation.

function calculateMortgagePayment() {}
Enter fullscreen mode Exit fullscreen mode

Inside the function, we collect the values that the user writes in the inputs using ".value".

  const borrowedMoney = amountInput.value
  const lengthOfLoan = lengthOfLoanInput.value * 12
  const interestRate = interestRateInput.value
  const calculedInterest = interestRate / 100
  const interestReady = calculedInterest / 12
Enter fullscreen mode Exit fullscreen mode

Also within the function, we begin to apply the formula to calculate our monthly bill.

  const percentage = interestReady
  const percentagePlusOne = interestReady + 1
  const exponentiationOperator = (percentagePlusOne ** lengthOfLoan)
  const firstDividend = percentage * exponentiationOperator
  const secondDividend = exponentiationOperator - 1
  const division = firstDividend / secondDividend
  const mortgage = borrowedMoney
  const quotas = mortgage * division
Enter fullscreen mode Exit fullscreen mode

And to complete our function, we show the results of the calculations.

  mortgageFinalResult.textContent = successMessage + quotas.toFixed(2)
  mortgageFinalResult.classList.add('success-message')
  calculateBtn.classList.add('form-success')
  calculateBtn.setAttribute('disabled','disabled')
  resetBtn.style.display = 'block'
Enter fullscreen mode Exit fullscreen mode

And… Voilà! Now, our mortgage calculation function should look like this.

function calculateMortgagePayment() {
  // We take initial values
  const borrowedMoney = amountInput.value
  const lengthOfLoan = lengthOfLoanInput.value * 12
  const interestRate = interestRateInput.value
  const calculedInterest = interestRate / 100
  const interestReady = calculedInterest / 12
  // We start the calculations
  const percentage = interestReady
  const percentagePlusOne = interestReady + 1
  const exponentiationOperator = (percentagePlusOne ** lengthOfLoan)
  const firstDividend = percentage * exponentiationOperator
  const secondDividend = exponentiationOperator - 1
  const division = firstDividend / secondDividend
  const mortgage = borrowedMoney
  const quotas = mortgage * division
  // And we show the results
  mortgageFinalResult.textContent = successMessage + quotas.toFixed(2)
  mortgageFinalResult.classList.add('success-message')
  calculateBtn.classList.add('form-success')
  calculateBtn.setAttribute('disabled','disabled')
  resetBtn.style.display = 'block'
}
Enter fullscreen mode Exit fullscreen mode

Once our function is ready, we prepare two constants, one for error and another one for success messages.

const errorMessage = 'There is an error in the form, please check it! 😥'
const successMessage = '🧮 Your monthly mortgage payment will be: '
Enter fullscreen mode Exit fullscreen mode

I add some quick validation for our mortgage form. You can find more information about this, for example, in MDN Web Docs by Mozilla.

amountInput.addEventListener('focusout',function(e){
    if (!amountInput.validity.valid) {
      amountInput.classList.add('error')
    } else {
        amountInput.classList.remove('error');
    }
})
interestRateInput.addEventListener('focusout',function(e){
    if (!interestRateInput.validity.valid) {
        interestRateInput.classList.add('error')
    } else {
        interestRateInput.classList.remove('error');
    }
})
lengthOfLoanInput.addEventListener('focusout',function(e){
    if (!lengthOfLoanInput.validity.valid) {
        lengthOfLoanInput.classList.add('error')
    } else {
        lengthOfLoanInput.classList.remove('error');
    }
})
Enter fullscreen mode Exit fullscreen mode

And the last step; an online form without buttons is not a form, don't you think? First a button for launching our function.

calculateBtn.addEventListener('click', function(e){
    if (amountInput.validity.valid && interestRateInput.validity.valid && lengthOfLoanInput.validity.valid) {
        calculateMortgagePayment()
    } else {
        mortgageFinalResult.textContent = errorMessage
        mortgageFinalResult.classList.add('error-message')
        calculateBtn.classList.add('form-error')
        if (!amountInput.validity.valid) {
            amountInput.classList.add('error')
        }
        if (!interestRateInput.validity.valid) {
            interestRateInput.classList.add('error')
        }
        if (!lengthOfLoanInput.validity.valid) {
            lengthOfLoanInput.classList.add('error')
        }
    }
})
Enter fullscreen mode Exit fullscreen mode

And the last one, another button to reset our form.

resetBtn.addEventListener('click', function() {
    resetBtn.style.display = 'none'
    mortgageFinalResult.textContent = ''
    calculateBtn.removeAttribute('disabled')
    calculateBtn.classList.remove('form-success')
})
Enter fullscreen mode Exit fullscreen mode

There we go! You can find all the lines of code in my repository on GitHub. Or test directly the mortgage calculator.


Sure there are best ways to get the same result, I know. I am just trying to share what I have learnt and, along the way, reinforce my own knowledge. If you know of a better way and want to share it here, I will be happy to read it in the comments.

Cheers! ♥️

💖 💪 🙅 🚩
bosspetta
Enrique

Posted on February 23, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related