Andrew Shearer
Andrew Shearer

Reputation: 366

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:

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

Answers (2)

ram
ram

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

A. Meshu
A. Meshu

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

Related Questions