Blog: Create a Cashflow Forecaster #5 (More Posts)
In this series, I am creating another app that mimics something I made on an Excel Spreadsheet. It’s an application that takes a starting figure and calculates changes in the forecast based on inputs and outputs on any given row of data.
You can follow the work I’ve done in my previous blogs in this series below.
- Create a Cashflow Forecaster #4
- Create a Cashflow Forecaster #3
- Create a Cashflow Forecaster #2
- Create a Cashflow Forecaster #1
In this part, we’re focusing on Calculating the Cashflow Totals. Below the main cashflow table is another DIV element cashflow_rowtotals. This has 5 child elements, 3 of which have class elements and text nodes of their own. The idea of this row is to calculate the total incoming values on any given cashflow forecast, the total outgoings, and the calculated difference between the 2 columns.
- .total_incoming
- .total_outgoing
- .total_balance
<div class="cashflow_rowtotals">
<span>Totals: </span>
<div class="total_incoming">£0.00</div>
<div class="total_outgoing">£0.00</div>
<div> </div>
<div class="total_balance">£0.00</div>
<div> </div>
<div> </div>
</div>
Let’s start with the incoming column.
<div class="total_incoming">£0.00</div>
We follow a similar development pattern here where first, we select the element we want to change and keep it with the other selectors in the script.
const totalIncomingElement = document.querySelector('.total_incoming');
Next, we define the function to add all the incoming values as edited in the cashflow table. We have written some code that iterates over these elements in the “Incoming” column, parsed their values (ensuring they are numbers), and added all them to the total variable.
Then we add the formatted calculated amount as the text content of .total_incoming.
// Function to calculate and display the total sum of the "Incoming" column
function calculateTotalIncoming() {
let total = 0;
incomingInputs.forEach(input => {
// Parse the value of each input and add it to the total
total += parseFloat(input.value) || 0;
});
// Update the total incoming element with the calculated sum
totalIncomingElement.textContent = `£${total.toFixed(2)}`;
}
We then call the function somewhere in the global scope.
calculateTotalIncoming();
Now if I then save the work I’ve done up to now and refresh the browser, that shows the correct result of the incoming values. And that’s all well and good. It’ll always show us the last saved value before any interactions but it will do no more than that. What if I want to see the calculation change in real-time when I’m editing the Incoming forecasts? We need to do a little more work to achieve this.
We add event listeners to each “incoming” input so that the total sum is recalculated and updated in real time as users change the values in the “incoming” column.
// Add event listeners to each incoming input to update the total when values changeincomingInputs.forEach(input => {
input.addEventListener('input', calculateTotalIncoming);
});
And now we should be seeing the total incomings calculation change in real time. Job done. We should do the same for the outgoing balance column.
The truth of the matter is, we’re basically defining the same function again but giving it an appropriately different name.
function calculateTotalOutgoing() {
// the function definition is exactly the same as calculateTotalIncoming()
}
Call the function to calculate and display the total incoming and outgoing values when the page loads
calculateTotalIncoming();
calculateTotalOutgoing();
And we shouldn’t forget the accompanying event listener, which handles real-time changes in the total amount.
outgoingInputs.forEach(input => {
input.addEventListener('input', calculateTotalOutgoing);
});
Calculate the difference with .total_balance
This is where we take the total incoming and outgoing amounts in the cashflow table and calculate the difference.
What we’re doing in the code below retrieving the values from the total incoming and total outgoing elements. Then we calculate the difference and update the total balance element accordingly.
function calculateTotalBalance() {
const totalIncoming = parseFloat(totalIncomingElement.textContent.replace('£', '')) || 0;
const totalOutgoing = parseFloat(totalOutgoingElement.textContent.replace('£', '')) || 0;
const totalBalance = totalIncoming - totalOutgoing;
totalBalanceElement.textContent = `£${totalBalance.toFixed(2)}`;
}
// Call the function to calculate and display the initial total balance
calculateTotalBalance();
Then we need to add the following event listeners to make sure the totals are updating in real time.
// Add event listeners to total incoming and total outgoing to update total balance
totalIncomingElement.addEventListener('DOMSubtreeModified', calculateTotalBalance);
totalOutgoingElement.addEventListener('DOMSubtreeModified', calculateTotalBalance);
We add event listeners to the total incoming and total outgoing elements to update the total balance whenever their contents change. We use the DOMSubtreeModified event to detect changes in the text content of these elements.
Added 2 new rows to the data table.
Sooner or later we’ll need to add more rows of the Cashflow Table. So this will be a bit of a test of how extensible and stable the application is.
In the code previously, we managed to get calculations achieving a smooth data flow with figures depending on the preceding row by calling the updateBalanceCell() function and giving it an index, referring to a specific table row. So it’s a simple case of passing as many unique indexes as there are table rows.
// Load the initial balances for each row when the page loads
window.addEventListener('load', function () {
//loadInitialBalances();
// Call updateFirstBalanceCell to calculate and display the balance initially
// previous function calls....
updateBalanceCell(4);
updateBalanceCell(5);
});
and again here on the startBalanceInput().
startingBalanceInput.addEventListener('load', function () {
// previous function calls....
updateBalanceCell(4);
updateBalanceCell(5);
});
So if there are 6 rows of data, the final index on the function call should be 5.
This completes the nuts and bolts of the application.
Let’s finish by summarising what has been achieved. The App
- Sets the starting balance for the spreadsheet and saves the value to localStorage.
- When the Starting balance is interacted with, the changed balance is reflected on all cells in the “Balance” column.
- All the values and balances save and persist across visits until interacted with again by the user.
- Changes to Estimated Balance Column toggling also persist.
- Dynamically calculate and update the difference between total outgoings and incomings.
In the final blog of the series, I’ll go through some UX improvements.


