I am trying to make a function for the following situation:
I have a number of user inputs that accept a number value from 0 to 100
If I entered 100 in input1 then input2 to input-n will not accept any number since the 100% is given to input1
If I entered 80 in input1 then input2 will be 20 and will not accept value above 20 If I entered 15 in input2 then the remaining 5 will go to input3
In my case I have 4 inputs but in the future those inputs may increase.
This is the function I have made so far:
function updatePayments() {
var payment1 = parseInt(document.getElementById('in-line1').value) || 0;
var payment2 = parseInt(document.getElementById('in-line2').value) || 0;
var payment3 = parseInt(document.getElementById('in-line3').value) || 0;
var payment4 = parseInt(document.getElementById('in-line4').value) || 0;
if (payment1 >= 100) {
payment2 = 0;
payment3 = 0;
payment4 = 0;
}
else {
var remainingPercent = 100 - payment1;
payment2 = remainingPercent;
if (payment3 > 0) {
payment2 = remainingPercent - payment3;
}
if (payment4 > 0) {
payment3 = remainingPercent - payment2 - payment4;
}
}
document.getElementById('in-line2').value = payment2;
document.getElementById('in-line3').value = payment3;
document.getElementById('in-line4').value = payment4;
var poNetTotalValue = GetPoNetTotalValue();
$('#out-line1').text(formatNumber(payment1 / 100 * poNetTotalValue));
$('#out-line2').text(formatNumber(payment2 / 100 * poNetTotalValue));
$('#out-line3').text(formatNumber(payment3 / 100 * poNetTotalValue));
$('#out-line4').text(formatNumber(payment4 / 100 * poNetTotalValue));
var totalPayments =
payment1 / 100 * poNetTotalValue +
payment2 / 100 * poNetTotalValue +
payment3 / 100 * poNetTotalValue +
payment4 / 100 * poNetTotalValue;
var submitButton = document.getElementById('btn-submit');
if (totalPayments != poNetTotalValue) {
$('#payment-check-validation').removeClass('visually-hidden');
submitButton.disabled = true;
}
else {
$('#payment-check-validation').addClass('visually-hidden');
submitButton.disabled = false;
}
$('#out-lines-total').text(formatNumber(totalPayments));
}
and this the HTML (its part of a cshtml)
<tbody>
<!-- line1: down payment -->
<tr>
<td width="50%">
Down Payment
<input type="hidden" asp-for="PoPayments[0].Notes" value="Down Payment"/>
</td>
<td width="20%">
<input id="in-line1"
asp-for="PoPayments[0].Percentage"
type="number" min="0" max="100"
class="w-100"
placeholder="%"
oninput="enforceMinMax(this); updatePayments()" />
</td>
<td>
<span id="out-line1" data-down-payment-value="0">
0
</span>
</td>
</tr>
<!-- line2: upon delivery -->
<tr>
<td width="50%">
Upon Delivery
<input type="hidden" asp-for="PoPayments[1].Notes" value="Upon Delivery" />
</td>
<td width="20%">
<input id="in-line2"
asp-for="PoPayments[1].Percentage"
type="number" min="0" max="100"
class="w-100"
placeholder="%"
oninput="enforceMinMax(this); updatePayments()">
</td>
<td>
<span id="out-line2" data-upon-delivery-value="0">
0
</span>
</td>
</tr>
<!-- line3: criteria1 -->
<tr>
<td width="50%">
<input type="text" asp-for="PoPayments[2].Notes"
placeholder="Enter Payment Criteria">
</td>
<td width="20%">
<input id="in-line3"
asp-for="PoPayments[2].Percentage"
type="number" min="0" max="100"
class="w-100"
placeholder="%"
oninput="enforceMinMax(this); updatePayments()">
</td>
<td>
<span id="out-line3" data-criteria1-value="0">
0
</span>
</td>
</tr>
<!-- line4: criteria2 -->
<tr>
<td width="50%">
<input type="text" asp-for="PoPayments[3].Notes"
placeholder="Enter Payment Criteria">
</td>
<td width="20%">
<input id="in-line4"
asp-for="PoPayments[3].Percentage"
type="number" min="0" max="100"
class="w-100"
placeholder="%"
oninput="enforceMinMax(this); updatePayments()">
</td>
<td>
<span id="out-line4" data-criteria2-value="0">
0
</span>
</td>
</tr>
</tbody>
function GetPoNetTotalValue() { return document.getElementById('poNetTotal').dataset.poNetTotalValue; }
this is just a function so that I don't have to repeat the same variable again and again and also if the element id changed I don't have to change it in many places because another part in the JS is using this value
as for the formatNumber and enforeMinMax
function formatNumber(num) {
return num.toLocaleString('en-US', { maximumFractionDigits: 2 });
}
function enforceMinMax(el) {
if (el.value != "") {
if (parseInt(el.value) < parseInt(el.min)) {
el.value = el.min;
el.dispatchEvent(new Event("input"));
}
if (parseInt(el.value) > parseInt(el.max)) {
el.value = el.max;
el.dispatchEvent(new Event("input"));
}
}
}
As you apparently use jQuery, I would use it to the full and not mix the standard DOM methods with it.
For the 4 inputs, I would suggest looping over them and keeping track of the value that is still available for the remaining inputs:
function updatePayments() {
const poNetTotalValue = GetPoNetTotalValue();
let totalPayments = 0;
let available = 100;
const $outs = $('#out-line1,#out-line2,#out-line3,#out-line4');
$('#in-line1,#in-line2,#in-line3,#in-line4').each(function (i) {
const value = +$(this).val();
const payment = Math.max(0, Math.min(available, value));
available -= payment;
if (value != payment) $(this).val(payment);
const out = payment / 100 * poNetTotalValue;
$outs.eq(i).text(formatNumber(out));
totalPayments += out;
});
const submitButton = $('#btn-submit');
$('#payment-check-validation').toggleClass('visually-hidden', totalPayments == poNetTotalValue);
submitButton.disabled = totalPayments !== poNetTotalValue;
$('#out-lines-total').text(formatNumber(totalPayments));
}
function enforceMinMax(el) {
if (el.value != "") {
if (+el.value < +el.min) {
el.value = el.min;
el.dispatchEvent(new Event("input"));
} else if (+el.value > +el.max) {
el.value = el.max;
el.dispatchEvent(new Event("input"));
}
}
}
function GetPoNetTotalValue() {
return $('#poNetTotal').data("poNetTotalValue") ?? 1;
}
function formatNumber(num) {
return num.toLocaleString('en-US', { maximumFractionDigits: 2 });
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<table>
<tbody>
<!-- line1: down payment -->
<tr>
<td width="50%">
Down Payment
<input type="hidden" asp-for="PoPayments[0].Notes" value="Down Payment"/>
</td>
<td width="20%">
<input id="in-line1"
asp-for="PoPayments[0].Percentage"
type="number" min="0" max="100"
class="w-100"
placeholder="%"
oninput="enforceMinMax(this); updatePayments()" />
</td>
<td>
<span id="out-line1" data-down-payment-value="0">
0
</span>
</td>
</tr>
<!-- line2: upon delivery -->
<tr>
<td width="50%">
Upon Delivery
<input type="hidden" asp-for="PoPayments[1].Notes" value="Upon Delivery" />
</td>
<td width="20%">
<input id="in-line2"
asp-for="PoPayments[1].Percentage"
type="number" min="0" max="100"
class="w-100"
placeholder="%"
oninput="enforceMinMax(this); updatePayments()">
</td>
<td>
<span id="out-line2" data-upon-delivery-value="0">
0
</span>
</td>
</tr>
<!-- line3: criteria1 -->
<tr>
<td width="50%">
<input type="text" asp-for="PoPayments[2].Notes"
placeholder="Enter Payment Criteria">
</td>
<td width="20%">
<input id="in-line3"
asp-for="PoPayments[2].Percentage"
type="number" min="0" max="100"
class="w-100"
placeholder="%"
oninput="enforceMinMax(this); updatePayments()">
</td>
<td>
<span id="out-line3" data-criteria1-value="0">
0
</span>
</td>
</tr>
<!-- line4: criteria2 -->
<tr>
<td width="50%">
<input type="text" asp-for="PoPayments[3].Notes"
placeholder="Enter Payment Criteria">
</td>
<td width="20%">
<input id="in-line4"
asp-for="PoPayments[3].Percentage"
type="number" min="0" max="100"
class="w-100"
placeholder="%"
oninput="enforceMinMax(this); updatePayments()">
</td>
<td>
<span id="out-line4" data-criteria2-value="0">
0
</span>
</td>
</tr>
</tbody>
</table>
<button id="btn-submit">Submit</button>