Check all input's validity first, then submit Vanilla JS

Viewed 29

I'm working on a form page that has multiple input fields. There is some error text to show if the inputs are not valid.

After the user submits all the input fields correctly I want to show a modal. The form validation is working almost fine except for the CVC input. It's only checking when the CVC field is empty.

What can I do to fix this?

const form = document.querySelector('form');
const inputs = document.querySelectorAll('input');
const modal = document.querySelector('.modal');
const modalClose = document.querySelector('.close');

form.addEventListener('submit',(e)=>{
    e.preventDefault();
    inputs.forEach(input=>{
        if(input.classList.contains('name-input')){
            containsNameInput(input)
        }else if(input.classList.contains('date')){
            containsDateInput(input)
        }else{
            containsOtherInput(input)
        }

        if(input.parentElement.classList.contains('error-empty')
        || input.parentElement.classList.contains('error-invalid')
        ||input.parentElement.parentElement.classList.contains('error-invalid')
        || input.parentElement.parentElement.classList.contains('error-empty')){
            modal.style.display = 'none';
        }else{
            modal.style.display = "block";
        }
    })
})

const containsNameInput = function(input){
    if(input.value === ''){
        input.parentElement.classList.add('error-empty');
        input.parentElement.classList.remove('error-invalid');
    }else if(input.value !== '' && input.value.match(/^[0-9]+$/) !== null){
        input.parentElement.classList.remove('error-empty');
        input.parentElement.classList.add('error-invalid');
    }
    else{
        input.parentElement.classList.remove('error-empty');
        input.parentElement.classList.remove('error-invalid');
    }
}

const containsDateInput = function(input){
    if(input.value === ''){
        input.parentElement.parentElement.classList.add('error-empty');
        input.parentElement.parentElement.classList.remove('error-invalid');
    }else if(input.value !== '' && input.value.match(/^[0-9]+$/) === null){  // wil return null if value has anything accept number
        input.parentElement.parentElement.classList.remove('error-empty');
        input.parentElement.parentElement.classList.add('error-invalid');
    }else{
        input.parentElement.parentElement.classList.remove('error-empty');
        input.parentElement.parentElement.classList.remove('error-invalid');
    }
}

const containsOtherInput = function(input){
    if(input.value === ''){
        input.parentElement.classList.add('error-empty');
        input.parentElement.classList.remove('error-invalid');
    }else if(input.value !== '' && input.value.match(/^[0-9]+$/) === null){
        input.parentElement.classList.remove('error-empty');
        input.parentElement.classList.add('error-invalid');
    }else{
        input.parentElement.classList.remove('error-empty');
        input.parentElement.classList.remove('error-invalid');
    }
}

modalClose.addEventListener('click',()=>{
    modal.style.display = "none";
})
.input-field .error{
    color: red;
    font-size: .7rem;
    display: none;
}
.input-field.error-empty .empty{
    display: block;
}
.input-field.error-invalid .invalid{
    display: block;
}
.modal{
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    display: none;
    background:white;
}
<form>
        <div class="input-field">
          <label for="name">Cardholder Name</label>
          <input type="text" placeholder="e.g. Jane Appleseed" id="name" name="name" maxlength="25" class="name-input">
          <p class="error empty">Can't be blank</p>
          <p class="error invalid">Wrong format, letters only</p>
        </div>
        <div class="input-field">
          <label for="number">Card Number</label>
          <input type="text" placeholder="e.g. 1234 5678 9123 0000" id="number" name="number" minlength="16" maxlength="16" class="number-input">
          <p class="error empty">Can't be blank</p>
          <p class="error invalid">Wrong format, numbers only</p>
        </div>
        <div class="flex-row">
          <div class="input-field date-container">
            <label id='date'>Exp. Date (MM/YY)</label>
            <div class="date-input">
              <input type='text' placeholder="MM" id='month' name="month" aria-labelledby='date' class="date month-input" maxlength="2"/>
              <input type='text' placeholder="YY" id='year' name="year" aria-labelledby='date' class="date year-input" maxlength="2"/>
            </div>
            <p class="error empty">Can't be blank</p>
            <p class="error invalid">Wrong format, numbers only</p>
          </div>
          <div class="input-field">
            <label for="cvc">CVC</label>
            <input type="text" placeholder="e.g. 123" id="cvc" name="cvc" maxlength="4" class="cvc-input">
            <p class="error empty">Can't be blank</p>
            <p class="error invalid">Wrong format, numbers only</p>
          </div>
        </div>
        <button type="submit" class="btn">Confirm</button>
      </form>
      <section class="modal">
        <img src="./images/icon-complete.svg" alt="">
        <h2>Thank you!</h2>
        <p>We've added your card details</p>
        <button type="button" class="btn close">Continue</button>
      </section>

2 Answers

html does all the blank validation by itself. i am adding required attribute on every input tag

const form = document.querySelector('form');
    const inputs = document.querySelectorAll('input');
    const modal = document.querySelector('.modal');
    const modalClose = document.querySelector('.close');

    form.addEventListener('submit', (e) => {
        e.preventDefault();
        inputs.forEach(input => {
            if (input.classList.contains('name-input')) {
                containsNameInput(input)
            } else if (input.classList.contains('date')) {
                containsDateInput(input)
            } else {
                containsOtherInput(input)
            }


        })
    })
    inputs.forEach(input => {
        if (input.parentElement.classList.contains('error-empty')
            || input.parentElement.classList.contains('error-invalid')
            || input.parentElement.parentElement.classList.contains('error-invalid')
            || input.parentElement.parentElement.classList.contains('error-empty')) {
            modal.style.display = "block";
        } else {
            modal.style.display = "none";
        }

    })

    const containsNameInput = function (input) {
        if (input.value === '') {
            input.parentElement.classList.add('error-empty');
            input.parentElement.classList.remove('error-invalid');
        } else if (input.value !== '' && input.value.match(/^[0-9]+$/) !== null) {
            input.parentElement.classList.remove('error-empty');
            input.parentElement.classList.add('error-invalid');
        }
        else {
            input.parentElement.classList.remove('error-empty');
            input.parentElement.classList.remove('error-invalid');
        }
    }

    const containsDateInput = function (input) {
        if (input.value === '') {
            input.parentElement.parentElement.classList.add('error-empty');
            input.parentElement.parentElement.classList.remove('error-invalid');
        } else if (input.value !== '' && input.value.match(/^[0-9]+$/) === null) {  // wil return null if value has anything accept number
            input.parentElement.parentElement.classList.remove('error-empty');
            input.parentElement.parentElement.classList.add('error-invalid');
        } else {
            input.parentElement.parentElement.classList.remove('error-empty');
            input.parentElement.parentElement.classList.remove('error-invalid');
        }
    }

    const containsOtherInput = function (input) {
        if (input.value === '') {
            input.parentElement.classList.add('error-empty');
            input.parentElement.classList.remove('error-invalid');
        } else if (input.value !== '' && input.value.match(/^[0-9]+$/) === null) {
            input.parentElement.classList.remove('error-empty');
            input.parentElement.classList.add('error-invalid');
        } else {
            input.parentElement.classList.remove('error-empty');
            input.parentElement.classList.remove('error-invalid');
        }
    }


    modalClose.addEventListener('click', () => {
        modal.style.display = "none";
    })
.input-field .error{
    color: red;
    font-size: .7rem;
    display: none;
}
.input-field.error-empty .empty{
    display: block;
}
.input-field.error-invalid .invalid{
    display: block;
}
.modal{
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    display: none;
    background:white;
}
<form>
        <div class="input-field">
          <label for="name">Cardholder Name</label>
          <input type="text" placeholder="e.g. Jane Appleseed" id="name" name="name" maxlength="25" class="name-input" >
          <p class="error empty">Can't be blank</p>
          <p class="error invalid">Wrong format, letters only</p>
        </div>
        <div class="input-field">
          <label for="number">Card Number</label>
          <input type="text" placeholder="e.g. 1234 5678 9123 0000" id="number" name="number" minlength="16" maxlength="16" class="number-input" >
          <p class="error empty">Can't be blank</p>
          <p class="error invalid">Wrong format, numbers only</p>
        </div>
        <div class="flex-row">
          <div class="input-field date-container">
            <label id='date'>Exp. Date (MM/YY)</label>
            <div class="date-input">
              <input type='text' placeholder="MM" id='month' name="month" aria-labelledby='date' class="date month-input" maxlength="2" />
              <input type='text' placeholder="YY" id='year' name="year" aria-labelledby='date' class="date year-input" maxlength="2"/>
            </div>
            <p class="error empty">Can't be blank</p>
            <p class="error invalid">Wrong format, numbers only</p>
          </div>
          <div class="input-field">
            <label for="cvc">CVC</label>
            <input type="text" placeholder="e.g. 123" id="cvc" name="cvc" maxlength="4" class="cvc-input" >
            <p class="error empty">Can't be blank</p>
            <p class="error invalid">Wrong format, numbers only</p>
          </div>
        </div>
        <button type="submit" class="btn">Confirm</button>
      </form>
      <section class="modal">
        <img src="./images/icon-complete.svg" alt="">
        <h2>Thank you!</h2>
        <p>We've added your card details</p>
        <button type="button" class="btn close">Continue</button>
      </section>

Classic bug: the result "variable" (here: visibility) gets set after every check, so it will contain the result of the last check and nothing else. Instead you should assume success at the beginning, and only set on failure of a check (and never set to success again). Then if any check fails, the entire thing will fail too.

const form = document.querySelector('form');
const inputs = document.querySelectorAll('input');
const modal = document.querySelector('.modal');
const modalClose = document.querySelector('.close');

form.addEventListener('submit', (e) => {
  e.preventDefault();
  modal.style.display = "block"; // assume things go well
  inputs.forEach(input => {
    if (input.classList.contains('name-input')) {
      containsNameInput(input)
    } else if (input.classList.contains('date')) {
      containsDateInput(input)
    } else {
      containsOtherInput(input)
    }

    if (input.parentElement.classList.contains('error-empty') ||
      input.parentElement.classList.contains('error-invalid') ||
      input.parentElement.parentElement.classList.contains('error-invalid') ||
      input.parentElement.parentElement.classList.contains('error-empty')) {
      modal.style.display = 'none'; // act when they do not
      //        }else{
      //            modal.style.display = "block";
    }
  })
})

const containsNameInput = function(input) {
  if (input.value === '') {
    input.parentElement.classList.add('error-empty');
    input.parentElement.classList.remove('error-invalid');
  } else if (input.value !== '' && input.value.match(/^[0-9]+$/) !== null) {
    input.parentElement.classList.remove('error-empty');
    input.parentElement.classList.add('error-invalid');
  } else {
    input.parentElement.classList.remove('error-empty');
    input.parentElement.classList.remove('error-invalid');
  }
}

const containsDateInput = function(input) {
  if (input.value === '') {
    input.parentElement.parentElement.classList.add('error-empty');
    input.parentElement.parentElement.classList.remove('error-invalid');
  } else if (input.value !== '' && input.value.match(/^[0-9]+$/) === null) { // wil return null if value has anything accept number
    input.parentElement.parentElement.classList.remove('error-empty');
    input.parentElement.parentElement.classList.add('error-invalid');
  } else {
    input.parentElement.parentElement.classList.remove('error-empty');
    input.parentElement.parentElement.classList.remove('error-invalid');
  }
}

const containsOtherInput = function(input) {
  if (input.value === '') {
    input.parentElement.classList.add('error-empty');
    input.parentElement.classList.remove('error-invalid');
  } else if (input.value !== '' && input.value.match(/^[0-9]+$/) === null) {
    input.parentElement.classList.remove('error-empty');
    input.parentElement.classList.add('error-invalid');
  } else {
    input.parentElement.classList.remove('error-empty');
    input.parentElement.classList.remove('error-invalid');
  }
}

modalClose.addEventListener('click', () => {
  modal.style.display = "none";
})
.input-field .error {
  color: red;
  font-size: .7rem;
  display: none;
}

.input-field.error-empty .empty {
  display: block;
}

.input-field.error-invalid .invalid {
  display: block;
}

.modal {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  display: none;
  background: white;
}
<form>
  <div class="input-field">
    <label for="name">Cardholder Name</label>
    <input type="text" placeholder="e.g. Jane Appleseed" id="name" name="name" maxlength="25" class="name-input">
    <p class="error empty">Can't be blank</p>
    <p class="error invalid">Wrong format, letters only</p>
  </div>
  <div class="input-field">
    <label for="number">Card Number</label>
    <input type="text" placeholder="e.g. 1234 5678 9123 0000" id="number" name="number" minlength="16" maxlength="16" class="number-input">
    <p class="error empty">Can't be blank</p>
    <p class="error invalid">Wrong format, numbers only</p>
  </div>
  <div class="flex-row">
    <div class="input-field date-container">
      <label id='date'>Exp. Date (MM/YY)</label>
      <div class="date-input">
        <input type='text' placeholder="MM" id='month' name="month" aria-labelledby='date' class="date month-input" maxlength="2" />
        <input type='text' placeholder="YY" id='year' name="year" aria-labelledby='date' class="date year-input" maxlength="2" />
      </div>
      <p class="error empty">Can't be blank</p>
      <p class="error invalid">Wrong format, numbers only</p>
    </div>
    <div class="input-field">
      <label for="cvc">CVC</label>
      <input type="text" placeholder="e.g. 123" id="cvc" name="cvc" minlength="3" maxlength="4" class="cvc-input">
      <p class="error empty">Can't be blank</p>
      <p class="error invalid">Wrong format, numbers only</p>
    </div>
  </div>
  <button type="submit" class="btn">Confirm</button>
</form>
<section class="modal">
  <img src="./images/icon-complete.svg" alt="">
  <h2>Thank you!</h2>
  <p>We've added your card details</p>
  <button type="button" class="btn close">Continue</button>
</section>

According to internet sources, CVC is either 3 or 4 digits, so added a minlength="3".

Related