简体   繁体   中英

Prevent the user from entering a number in the input field if a certain number of them already have a value

I am currently working on a fee calculator. Here are the rules for the part of it that I am working on:

  • There are 9 number input fields, A through I
  • Input fields D, E, & F are mandatory. The user MUST input a number (greater than 0) in AT LEAST 1 of these 3 input boxes (I have taken care of this aspect already)
  • The user can only enter a number (greater than 0, no limit) into up to 6 of the 9 inputs, maximum

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;
          });
        }
      });
    });

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')
      );
    }
  });
});

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>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM