Custom HTML5 Data Validation

Category: Form , Javascript | January 14, 2018
Author: Laurent G.
Views Total: 228
Official Page: Go to website
Last Update: January 14, 2018
License: MIT

Preview:

Custom HTML5 Data Validation

Description:

A small form validation script that automatically highlights invalid form fields and displays custom error messages using HTML5 validation attributes.

How to use it:

Add required and error message to the form fields using the following data attributes:

<form class="form" action="#_">
  <fieldset>
    <legend>My informations:</legend>
    <p class="requirement"><em>Fields followed by (*) are required</em></p>
    
    <div class="form-field">
      <label id="optional-label" for="optional">Optional field:</label>
      <input id="optional" name="optional" type="text" placeholder="optional">
    </div>
    <div class="form-field">
      <label id="lastname-label" for="lastname">Lastname: <span aria-hidden="true">(*)</span></label>
      <input id="lastname" name="lastname" type="text" placeholder="Dupond" required
             pattern="[a-zA-Zçàéèëìîïòõôûùñ '-]+" 
             title="Lettres uniquement"
             aria-labelledby="lastname-label" 
             data-msg-required="The Lastname field is required." 
             data-msg-error="The Lastname field should contain only letters.">
    </div>

    <div class="form-field">
      <label id="firstname-label" for="firstname">Firstname: <span aria-hidden="true">(*)</span></label>
      <input id="firstname" name="firstname" type="text" placeholder="Jean" required 
             pattern="[a-zA-Zçàéèëìîïòõôûùñ' -]+" 
             title="Lettres uniquement"
             aria-labelledby="firstname-label" 
             data-msg-required="The Firstname field is required." 
             data-msg-error="The Firstname field should contain only letters.">
    </div>

    <div class="form-field">
      <label id="email-label" for="email">Email: <span aria-hidden="true">(*)</span></label>
      <input id="email" type="email" placeholder="[email protected]" required 
             aria-labelledby="email-label" 
             data-msg-required="The Email field is required." 
             data-msg-error="The Email field is invalid">
    </div>

    <div class="form-field">
      <label id="phone-label" for="phone">Phone number: (0611223344 or +33611223344) <span aria-hidden="true">(*)</span></label>
      <input id="phone" name="phone" type="tel" placeholder="0611223344" required 
             pattern="^((\+\d{1,3}(-| )?\(?\d\)?(-| )?\d{1,5})|(\(?\d{2,6}\)?))(-| )?(\d{3,4})(-| )?(\d{4})(( x| ext)\d{1,5}){0,1}$"  
             aria-labelledby="phone-label" 
             data-msg-required="The Phone field is required." 
             data-msg-error="The Phone field is invalid. It must contain only numbers and/or the sign '+'">
    </div>
    <div class="form-field">
      <label id="civility-label" for="civility">Civility: <span aria-hidden="true">(*)</span></label>
      <select id="civility" name="civility" required
              aria-labelledby="civility-label"
              data-msg-required="The Civility field is required.">
        <option value="" disabled selected>Select an option</option>
        <option value="1">Mrs.</option>
        <option value="2">Mr.</option>
      </select>
    </div>
    
    <div class="form-field">
      <label id="password-label" for="password">Password: (<abbr tabindex="0" title="required">*</abbr>)
      <span class="input-help">Password must be 8+ characters and include letters (at least one Uppercase) AND number(s)</span></label>
      <input id="password" name="password" type="password" required 
             pattern="(?=^.{8,}$)((?=.*\d)|(?=.*\W+))(?![.\n])(?=.*[A-Z])(?=.*[a-z]).*$"
             aria-labelledby="password-label"
             data-msg-required="The Password field is required."
             data-msg-error="Password field is invalid. It must be 8+ characters and include letters (at least one Uppercase) AND number(s)">
    </div>
    
    <div class="form-field">
      <label id="radio-label" for="radio">
        <input type="radio" id="radio" name="radio" required
               aria-labelledby="radio-label"
               data-msg-required="The Password field is required.">
        Radio
    </div>
    
    <div class="form-submit">
      <input type="reset">
      <input type="submit">
    </div>
  </fieldset>
</form>

The main JavaScript to activate the custom HTML5 form validator.

"use strict";

const inputs = document.querySelectorAll('input[required],select[required],textarea[required]'),
      oForm = document.querySelector('.form'), 
      defaultRequiredMsg = "This field is required.",
      defaultErrorMsg = "This field is invalid.";

function formCheck(input){
  const inputParent = input.closest('.form-field'),
        inputId = input.id;
  let errorMsg = input.getAttribute('data-msg-error'),
      requireMsg = input.getAttribute('data-msg-required');
  let label_error;
  input.setAttribute("aria-invalid", !input.checkValidity());
  if(!input.checkValidity()) {
    if(!document.querySelector('#'+inputId+"-error")){
      label_error = document.createElement('p');
      label_error.setAttribute("id", inputId+"-error");
      label_error.setAttribute('class','msg-form-error');
      inputParent.insertBefore(label_error, input.nextSibling);
    } else {
      label_error = document.querySelector('#'+inputId+"-error");
    }
    if (input.value === ''){
      requireMsg ? requireMsg = requireMsg : requireMsg = defaultRequiredMsg;
      label_error.innerHTML = requireMsg;
    }else{
      errorMsg ? errorMsg = errorMsg : errorMsg = defaultErrorMsg;
      label_error.innerHTML = errorMsg;
    }
    inputParent.classList.add('is-invalid');
    inputParent.classList.remove('is-valid');
    input.setAttribute('aria-describedby',inputId+'-error');
  } else {
    if(document.querySelector('#'+inputId+"-error")){
      label_error = document.querySelector('#'+inputId+"-error");
      label_error.parentNode.removeChild(label_error);
    }
    inputParent.classList.remove('is-invalid');
    inputParent.classList.add('is-valid');
    input.removeAttribute('aria-describedby');
  }
}

// On BLUR : check the input blured
inputs.forEach(input => {
  input.addEventListener(
    "blur", event => {
      formCheck(input);
    },
    false
  );
});

// On SUBMIT :
oForm.querySelector('[type="submit"]').addEventListener("click", function(e){
  oForm.classList.add('is-submited');
  const iLength = inputs.length;
  inputs.forEach((input,index) => {
    formCheck(input);
    if(index >= iLength-1){
      const invalidInputs = oForm.querySelectorAll(".is-invalid input, .is-invalid select");
      if(invalidInputs.length > 0){
        invalidInputs[0].focus();
      }
    }
    input.addEventListener(
      "invalid", event => {
        event.preventDefault();
      },
      false
    );
  });
});

// On RESET :
oForm.querySelector('[type="reset"]').addEventListener("click", function(e){
  oForm.classList.remove('is-submited');
  inputs.forEach(input => {
    let inputParent = input.closest('.form-field');
    //inputs[0].focus();
    inputParent.classList.remove('is-valid');
    inputParent.classList.remove('is-invalid');
  });
  let msgErrors = document.querySelectorAll('.msg-form-error');
  msgErrors.forEach(msgError => {
    msgError.parentNode.removeChild(msgError);
  });
});

Leave a Reply