Reputation: 366
I am currently working on a fee calculator. Here are the rules for the part of it that I am working on:
Something I have already achieved with this app, I have a few check boxes on the page, and the user must select at least 1, up to 5 maximum. Once a user checks 5 of those boxes, they cannot check a 6th. If they un-check one of the 5, they can re-check a different one.
So, I am looking to achieve a similar effect with these number inputs.
Once the user has entered a number (again, greater than 0) in up to 6 of the 9 input fields, they cannot enter something in the one of the 3 remaining inputs. But if they were to remove their input from one of the 6 fields, they should be able to enter a number in one of the 4 other inputs, as now only 5 of them have something entered. Again this number can be any value greater than 0. It could be 10, 10,000 or 100,000, etc. The total values across the inputs doesn't matter, just HOW MANY of the 9 inputs you put a value in (again up to 6 maximum).
I am NOT asking for help with the calculations themselves, nor am I asking for help with the check boxes. I am just wanting some help on the functionality mentioned in the paragraph above.
Also, this must be done in plain vanilla JavaScript, no jQuery.
Any help finding this solution would be much appreciated! Thanks!
Here is the HTML:
<div>
<div>
<label for="Input A">Input A</label>
<input class="entity-input license-input" type="number" name="Input A" value="0" min="0">
</div>
<div>
<label for="Input B">Input B</label>
<input class="entity-input license-input" type="number" name="Input B" value="0" min="0">
</div>
<div>
<label for="Input C">Input C</label>
<input class="entity-input license-input" type="number" name="Input C" value="0" min="0">
</div>
<div>
<label for="Input D">Input D</label>
<input class="entity-input license-input-mandatory" type="number" name="Input D" value="0" min="0">
</div>
<div>
<label for="Input E">Input E</label>
<input class="entity-input license-input-mandatory" type="number" name="Input E" value="0" min="0">
</div>
<div>
<label for="Input F">Input F</label>
<input class="entity-input license-input-mandatory" type="number" name="Input F" value="0" min="0">
</div>
<div>
<label for="Input G">Input G</label>
<input class="entity-input distribution-input" type="number" name="Input G" value="0" min="0">
</div>
<div>
<label for="Input H">Input H</label>
<input class="entity-input distribution-input" type="number" name="Input H" value="0" min="0">
</div>
<div>
<label for="Input I">Input I</label>
<input class="entity-input distribution-input" type="number" name="Input I" value="0" min="0">
</div>
</div>
And here is the JavaScript I have so far:
// Select all elements with class of entity-input
const ENTITY_INPUTS = document.querySelectorAll('.entity-input');
// Prevent user from entering a number on 7th number input (cannot fill in more than 6)
ENTITY_INPUTS.forEach((input) => {
const MAX = 6;
// Upon leaving the input, assign a data-changed attr with a value of true or false depending on whether the value has changed
input.addEventListener('blur', () => {
if (input.value == 0) {
input.removeAttribute('data-changed', 'true');
input.setAttribute('data-changed', 'false');
} else if (input.value !== 0) {
input.removeAttribute('data-changed', 'false');
input.setAttribute('data-changed', 'true');
}
let unchangedInputs = document.querySelectorAll('[data-changed="false"]');
if (unchangedInputs.length !== []) {
console.log(`The number of inputs with a value still at zero is ${unchangedInputs.length}`);
}
});
// Count the number of inputs with data-changed set to true - can't be more than 6
input.addEventListener('focus', () => {
let changedInputs = document.querySelectorAll('[data-changed="true"]');
console.log(`The number of inputs with a value more than zero is ${changedInputs.length}`);
if (changedInputs.length == MAX && input.value > 0) {
console.log(`You may change this element`);
} else if (changedInputs.length == MAX) {
console.log(`You can't enter any more numbers!`);
}
});
});
EDIT: I was able to solve this after some slight modifications to my HTML and JS.
I gave all 9 inputs the attribute data-changed="false"
by default, instead of having it assigned dynamically based on user input. And similar to @7iiBob's answer, I put everything into blur
, and I got the effect I needed:
ENTITY_INPUTS.forEach((input) => {
const REMAINING_INPUTS = 3;
// Upon leaving the input, assign a data-changed attr with a value of true or false depending on whether the value has changed
input.addEventListener('blur', () => {
if (input.value == 0) {
input.removeAttribute('data-changed', 'true');
input.setAttribute('data-changed', 'false');
} else if (input.value !== 0) {
input.removeAttribute('data-changed', 'false');
input.setAttribute('data-changed', 'true');
}
// upon leaving, check number of elements still with data-changed set to false
// if the number of elements is equal to 3, set them to disabled
// else, leave them alone (set disabled to false)
let unchangedInputs = document.querySelectorAll('[data-changed="false"]');
if (unchangedInputs.length == REMAINING_INPUTS) {
unchangedInputs.forEach((input) => {
input.disabled = true;
});
} else {
unchangedInputs.forEach((input) => {
input.disabled = false;
});
}
});
});
Upvotes: 0
Views: 1366
Reputation: 690
You look pretty darn close to having this solved.
Why not put everything into blur
?
// Select all elements with class of entity-input
const ENTITY_INPUTS = document.querySelectorAll('.entity-input');
// Prevent user from entering a number on 7th number input (cannot fill in more than 6)
ENTITY_INPUTS.forEach(input => {
const MAX = 6;
// Upon leaving the input, assign a data-changed attr with a value of true or false depending on whether the value has changed
input.addEventListener('blur', () => {
if (input.value == 0) {
input.removeAttribute('data-changed', 'true');
input.setAttribute('data-changed', 'false');
} else if (input.value !== 0) {
input.removeAttribute('data-changed', 'false');
input.setAttribute('data-changed', 'true');
}
let changedInputs = document.querySelectorAll('[data-changed="true"]');
let unchangedInputs = document.querySelectorAll('[data-changed="false"]');
if (changedInputs.length == MAX) {
unchangedInputs.forEach(inputToDisable =>
inputToDisable.setAttribute('disabled', 'true')
);
} else if (changedInputs.length < MAX) {
unchangedInputs.forEach(inputToEnable =>
inputToEnable.setAttribute('disabled', 'false')
);
}
});
});
Upvotes: 2
Reputation: 4148
This is the logic.
Implant on checkboxes:
let inputCheckboxesLength = 0; // initial counter to 0
const inputCheckboxes = document.querySelectorAll('#inputCheck input'); // target the checkboxes
for (var i=0; i < inputCheckboxes.length; i++) { // iterate checkboxes
inputCheckboxes[i].addEventListener('change', function() { // listen to changne event:
if (this.checked) { // if one of the checkboxes selected:
++inputCheckboxesLength; // increase the count
if (inputCheckboxesLength === 6) { // if the count more then 5 (equal to 6)
alert ('You cannot check more then 5 checkboxes!'); // alert error message
inputCheckboxesLength = 5; // change the count back to 5
this.checked = false; // remove the checked for the last checkbox
}
}
else {
--inputCheckboxesLength // decrease the count - will tirgger when user remove check-mark from checkbox
}
});
}
<fieldset id="inputCheck">
<label for="check1">1<input type="checkbox" id="check1" /></label>
<label for="check2">2<input type="checkbox" id="check2" /></label>
<label for="check3">3<input type="checkbox" id="check3" /></label>
<label for="check4">4<input type="checkbox" id="check4" /></label>
<label for="check5">5<input type="checkbox" id="check5" /></label>
<label for="check6">6<input type="checkbox" id="check6" /></label>
<label for="check7">7<input type="checkbox" id="check7" /></label>
<label for="check8">8<input type="checkbox" id="check8" /></label>
</fieldset>
Implant on inputs:
let inputNumberLength = 0; // initial counter to 0
const inputNumbers = document.querySelectorAll('#inputNumber input'); // target the inputs
for (var i=0; i < inputNumbers.length; i++) { // iterate inputs
inputNumbers[i].addEventListener('change', function() { // listen to changne event:
if (this.value.length > 0) {
++inputNumberLength; // increase the count
if (inputNumberLength === 6) { // if the count more then 5 (equal to 6)
alert ('You cannot put more then 5 values!'); // alert error message
inputNumberLength = 5; // change the count back to 5
this.value = ''; // remove the value for the last input
}
}
else {
--inputNumberLength // decrease the count - will tirgger when user remove check-mark from checkbox
}
});
}
<fieldset id="inputNumber">
<label for="a"><input type="number" id="a" /></label>
<label for="b"><input type="number" id="b" /></label>
<label for="c"><input type="number" id="c" /></label>
<label for="d"><input type="number" id="d" /></label>
<label for="e"><input type="number" id="e" /></label>
<label for="f"><input type="number" id="f" /></label>
<label for="g"><input type="number" id="g" /></label>
<label for="h"><input type="number" id="h" /></label>
<label for="i"><input type="number" id="i" /></label>
</fieldset>
Upvotes: 0