Jonnie Grieve Digital Media: Blog

Home
by on 13th December, 2023 - 2:36pm (0)

Blog: A Code Walkthrough – Live on a Million (More Posts)

I’m building an application that calculates how long a person can live on a given amount of money in a given number of years.  Say, for example, £1million for the rest of your years.  It’s just for a bit of fun and makes certain assumptions, like, no accruing of savings interests or other opportunities of personal income – and doesn’t cover one-time only expenses. It’s definitely not a substitute for financial advice  🙂

You fill in some initial text inputs and add as many more as you think you’ll need to live on through your life and select the number of years and see how this will affect your starting balance.

Let’s go through it up bit by bit.

We start with 6 input and label elements.  Most of them are an initial list of annual expenses, each of which is editable. In the input fields you can enter any positive number.  The remaining input element governs the number of years your expenses forecast will cover.

<?php require "header.php"; ?>

<h2 id="accumulated_total">£0.00</h2>

<main id="container">

<aside>
    <span>Number of Years: </span>
    <input type="number" id="num_years" class="num_years" min="0" value="£0.00" />
</aside>


<section class="expense_list">

    <label for="" contenteditable="true">Electric</label>
    <input type="number" id="" class="expense_value" min="0" value="0">
    <p></p>

    <label for="" contenteditable="true">Gas</label>
    <input type="number" id="" class="expense_value" min="0" value="0">
    <p></p>

    <label for="" contenteditable="true">Water</label>
    <input type="number" id="" class="expense_value" min="0" value="0">
    <p></p>

    <label for="" contenteditable="true">Council Tax</label>
    <input type="number" id="" class="expense_value" min="0" value="0">
    <p></p>

    <label for="" contenteditable="true">TV</label>
    <input type="number" id="" class="expense_value" min="0" value="0">
    <p></p>

    <a href="" class="btn" id="btn_addNew">Add New Expense</a>

</section>

<section class="save_list">
    <a href="#" class="btn" id="btn_save">Save</a>
</section>

</main>

<?php require "footer.php"; ?>

Soon, we’ll be working with dynamic elements and changing data.  But for now, we’ll start by calculating the total sum of the first 5 input elements. (.expense_value)

document.addEventListener('DOMContentLoaded', function() {
    // Get all input elements with the class 'expense_value'
    const expenseInputs = document.querySelectorAll('.expense_value');

    // Add an input event listener to each expense input
    expenseInputs.forEach(function(input) {
        input.addEventListener('input', updateAccumulatedTotal);
    });

    // Initial update of the accumulated total
    updateAccumulatedTotal();

    function updateAccumulatedTotal() {
        // Get the accumulated total element
        const accumulatedTotalElement = document.getElementById('accumulated_total');

        // Sum the values of all expense input fields
        const totalValue = Array.from(expenseInputs).reduce(function(sum, input) {
            return sum + parseFloat(input.value || 0);
    }, 0);

    // Update the accumulated total element
        accumulatedTotalElement.textContent = `£${totalValue.toFixed(2)}`;
    }
});

This takes the value of each of the input elements and returns to sum, an integer to the h2 element with the ID “accumulated_total“. And the number changes dynamically according to changes in each of the input elements.

What this means is we know that the total of the inputs is set up correctly and updated in real-time. But what we need is to have a constant variable, which is set to an integer, in this case, 1 million. That value will be the value of #accumulated_total – that is to say the highest possible value; a million. And then take the sum of the input fields and take that sum away from #accumulated_total.

 

// Set the constant value
const initialTotal = 1000000; // 1 million

function updateAccumulatedTotal() {
    // Sum the values of all expense input fields
    const totalValue = Array.from(expenseInputs).reduce(function(sum, input) {
        return sum + parseFloat(input.value || 0);
    }, 0);

    // Calculate the remaining value
    const remainingValue = initialTotal - totalValue;

    // Update the accumulated total element
    accumulatedTotalElement.textContent = `£${remainingValue.toFixed(2)}`;
}

Now, the #accumulated_total will start with 1 million, and as you update the input fields, it will subtract the sum of the input fields from the initial total.

It’s also worth having a calculated total of all annual expenses, just like we started with before.

I’ve added a new element that goes below the “Add New Expense” button,  (.btn_addNew) button.

<div class="sum_total">Total: Items 
    <span id="amount">£0</span>
</div>

This element will display the sum total of all the input fields just like we did before but it will leave the #accumulated_total as it is now so 2 numbers that are updated in real time.

It gets more complicated from here because we’re going to be starting working with dynamic and changing data.

We’ve got our set of 5 expense totals from what is included in the DOM. Now, let’s multiply it with another input element that lets you select the number of years you forecast that you want to live your million on.

<aside>

    <span>Number of Years: </span>
    <input type="number" id="num_years" class="num_years" min="0" value="£0.00" />

</aside> 

 

// Get the num_years input element
const numYearsInput = document.getElementById('num_years');

We then need to modify the accumulatedTotal function to multiply the million, by the calculated difference number of years.

function updateAccumulatedTotal() {
    // Get the value of #num_years
    const numYearsValue = parseFloat(numYearsInput.value) || 0;

    // Get all input elements with the class 'expense_value'
    const expenseInputs = document.querySelectorAll('.expense_value');

    // Sum the values of all expense input fields
    const totalValue = Array.from(expenseInputs).reduce(function(sum, input) {
        return sum + parseFloat(input.value || 0);
    }, 0);

   // Calculate the remaining value considering num_years
   const remainingValue = initialTotal - (totalValue * numYearsValue);

   // Update the accumulated total element
   accumulatedTotalElement.textContent = `£${remainingValue.toFixed(2)}`;

   // Update the sum total element
   sumTotalElement.textContent = `£${totalValue.toFixed(2)}`;
}

You can modify the script to take into account the value of #num_years and multiply it by the sum total of the input fields.

Now we’re going to need to add the ability to add new items to the totals list whilst also keeping the sum_total and accumulated_totals up to date. This is the tricky part because There’s a lot that can go wrong here since we’re dynamically adding new elements to the DOM.

Let’s start by selecting  #btn_addNewand making sure the button no longer acts like a link and just adds a new label and input element.

Like this .

<label for="" contenteditable="true">TV</label>
<input type="number" id="" class="expense_value" min="0" value="0">
<p></p>
function addNewExpense() {
    // Create a new label element
    const newLabel = document.createElement('label');
    newLabel.setAttribute('for', '');
    newLabel.setAttribute('contenteditable', 'true');
    newLabel.textContent = 'New Expense';

    // Create a new input element
    const newInput = document.createElement('input');
    newInput.setAttribute('type', 'number');
    newInput.setAttribute('class', 'expense_value');
    newInput.setAttribute('min', '0');
    newInput.setAttribute('value', '0');
    newInput.addEventListener('input', updateAccumulatedTotal);

    // Create a new paragraph element
    const newParagraph = document.createElement('p');

    // Insert the new elements before the add new button
    const expenseList = document.querySelector('.expense_list');
    expenseList.insertBefore(newLabel, addNewButton);
    expenseList.insertBefore(newInput, addNewButton);
    expenseList.insertBefore(newParagraph, addNewButton);

    // Trigger an input event to update totals
    updateAccumulatedTotal();
}

Now we have a fully functional single-page application that allows you to add in as many forecasted spending items that you like and it will work regardless of whether you added them to the application or not. Where we run into problems is where we try to implement save functionality which causes some issues with data not syncing properly. A problem to be tackled another day.

You can view the final version of the application here.

This post has been assigned to the following categories

    Leave a Reply

    Your email address will not be published. Required fields are marked *