Form Validation

8 minute read

Published:

This lesson covers Client-side form validation.

  • Client-side validation

    • initial check - an important feature of good user experience
    • catching invalid data on the client-side so that the user can fix it straight away.
    • If it gets to the server and is then rejected, a noticeable delay is caused by a round trip to the server and then back to the client-side to tell the user to fix their data.
    • should not be considered an exhaustive security measure
    • perform security checks on any form-submitted data on the server-side as well as the client-side, because client-side validation is too easy to bypass, so malicious users can still easily send bad data through to your server.
  • Example

    • “This field is required” (You can’t leave this field blank).
    • “Please enter your phone number in the format xxx-xxxx” (A specific data format is required for it to be considered valid).
    • “Please enter a valid email address” (the data you entered is not in the right format).
    • “Your password needs to be between 8 and 30 characters long and contain one uppercase letter, one symbol, and a number.” (A very specific data format is required for your data).
  • Different types of client-side validation

    • Built-in form validation
      • uses HTML5 form validation features - has better performance than JavaScript, but it is not as customizable as JavaScript validation.
    • JavaScript
      • validation is coded using JavaScript. This validation is completely customizable, but you need to create it all (or use a library).
  • Using built-in form validation

    • required: Specifies whether a form field needs to be filled in before the form can be submitted.
    • minlength and maxlength: Specifies the minimum and maximum length of textual data (strings)
    • min and max: Specifies the minimum and maximum values of numerical input types
    • type: Specifies whether the data needs to be a number, an email address, or some other specific preset type.
    • pattern: Specifies a regular expression that defines a pattern the entered data needs to follow.
  • Example

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="utf-8">
        <title>Form Validation</title>
      
        <style>
          input:invalid{
            border: 2px dashed red;
          }
          input:valid{
            border: 2px solid black;
          }
        </style>
      
      
      </head>
      <body align="center">
        <form>
      
          <input type="text" name="name" id='name' placeholder="Enter your name" required> <br/><br/>
      
          <input type="email" name="email" id='email' placeholder="Enter your email" required> <br/><br/>
      
          <input type="password" name="password" id='password' placeholder="Enter your password" required> <br/><br/>
      
          <button type="button" name="button">Submit</button>
        </form>
      
      </body>
    </html>
    
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="utf-8">
        <title>Form Validation</title>
      
        <style>
          input:invalid{
            border: 2px dashed red;
          }
          input:valid{
            border: 2px solid black;
          }
        </style>
      
      
      </head>
      <body align="center">
        <form>
      
          <label for='name'>Name: </label>
          <input type="text" name="name" id='name' required> <br/><br/>
      
          <label for='email'>Email: </label>
          <input type="email" name="email" id='email' required> <br/><br/>
      
          <label for='password'>Password: </label>
          <input type="password" name="password" id='password' required> <br/><br/>
      
          <label for='age'>Age: </label>
          <input type="number" name="age" id='age' value="1" min="14"> <br/><br/>
      
          <button type="button" name="button">Submit</button>
        </form>
      
      </body>
    </html>
      
    

  • Validating against a regular expression

    • a — Matches one character that is a (not b, not aa, and so on). abc — Matches a, followed by b, followed by c. ab?c—Matches a, optionally followed by a single b, followed by c. ( ac or abc) ab*c—Matches a, optionally followed by any number of bs, followed by c. ( ac , abc, abbbbbc, and so on). a|b — Matches one character that is a or b. abc|xyz — Matches exactly abc or exactly xyz (but not abcxyz or a or y, and so on).

    • <!DOCTYPE html>
      <html>
          
      <head>
          <meta charset="utf-8">
          <title>Favorite fruit with required and pattern attributes</title>
          <style>
            input:invalid {
              border: 2px dashed red;
            }
          
            input:valid {
              border: 2px solid black;
            }
          </style>
      </head>
          
      <body>
          <form>
            <label for="choose">Would you prefer a banana or a cherry?</label>
                
            <input id="choose" name="i_like" required pattern="[Bb]anana|[Cc]herry">
                
            <button>Submit</button>
          </form>
      </body>
          
      </html>
      
    • Constraining the values of your entries

      • <!DOCTYPE html>
        <html>
              
        <head>
            <meta charset="utf-8">
            <title>Favorite fruit start</title>
            <style>
              input:invalid {
                border: 2px dashed red;
              }
              
              input:valid {
                border: 2px solid black;
              }
            </style>
        </head>
              
        <body>
            <form>
                    
          <div>
            <label for="choose">Would you prefer a banana or a cherry?</label>
            <input type="text" id="choose" name="i_like" required minlength="6" maxlength="6">
          </div>
          <div>
            <label for="number">How many would you like?</label>
            <input type="number" id="number" name="amount" value="1" min="1" max="10">
          </div>
          <div>
            <button>Submit</button>
          </div>
        </form>
                
        </body>
              
        </html>
        
    • Full Example, https://github.com/mdn/learning-area/blob/master/html/forms/form-validation/full-example.html

      • <!DOCTYPE html>
        <html>
              
        <head>
            <meta charset="utf-8">
            <title>Full built-in validation example</title>
            <style>
              form {
                font: 1em sans-serif;
                max-width: 320px;
              }
              
              p > label {
                display: block;
              }
              
              input[type="text"],
              input[type="email"],
              input[type="number"],
              textarea,
              fieldset {
                width : 100%;
                border: 1px solid #333;
                box-sizing: border-box;
              }
              
              input:invalid {
                box-shadow: 0 0 5px 1px red;
              }
              
              input:focus:invalid {
                box-shadow: none;
              }
            </style>
        </head>
              
        <body>
          <form>
            <p>
              <fieldset>
                <legend>Do you have a driver's license?<abbr title="This field is mandatory" aria-label="required">*</abbr></legend>
                <!-- While only one radio button in a same-named group can be selected at a time,
                     and therefore only one radio button in a same-named group having the "required"
                     attribute suffices in making a selection a requirement -->
                <input type="radio" required name="driver" id="r1" value="yes"><label for="r1">Yes</label>
                <input type="radio" required name="driver" id="r2" value="no"><label for="r2">No</label>
              </fieldset>
            </p>
            <p>
              <label for="n1">How old are you?</label>
              <!-- The pattern attribute can act as a fallback for browsers which
                   don't implement the number input type but support the pattern attribute.
                   Please note that browsers that support the pattern attribute will make it
                   fail silently when used with a number field.
                   Its usage here acts only as a fallback -->
              <input type="number" min="12" max="120" step="1" id="n1" name="age"
                     pattern="\d+">
            </p>
            <p>
              <label for="t1">What's your favorite fruit?<abbr title="This field is mandatory" aria-label="required">*</abbr></label>
              <input type="text" id="t1" name="fruit" list="l1" required
                     pattern="[Bb]anana|[Cc]herry|[Aa]pple|[Ss]trawberry|[Ll]emon|[Oo]range">
              <datalist id="l1">
                <option>Banana</option>
                <option>Cherry</option>
                <option>Apple</option>
                <option>Strawberry</option>
                <option>Lemon</option>
                <option>Orange</option>
              </datalist>
            </p>
            <p>
              <label for="t2">What's your e-mail address?</label>
              <input type="email" id="t2" name="email">
            </p>
            <p>
              <label for="t3">Leave a short message</label>
              <textarea id="t3" name="msg" maxlength="140" rows="5"></textarea>
            </p>
            <p>
              <button>Submit</button>
            </p>
          </form>
        </body>
              
        </html>
        

Validating forms using JavaScript

  • The Constraint Validation API

    • https://github.com/mdn/learning-area/blob/master/html/forms/form-validation/custom-error-message.html

      <!DOCTYPE html>
      <html lang="en">
        <head>
          <meta charset="utf-8">
          <title>Form Validation</title>
          
          <style>
            input:invalid{
              border: 2px dashed red;
            }
            input:valid{
              border: 2px solid black;
            }
          </style>
          
          
        </head>
        <body align="center">
          <form>
          
            <label for='name'>Name: </label>
            <input type="text" name="name" id='name'> <br/><br/>
          
            <label for='email'>Email: </label>
            <input type="email" name="email" id='email'> <br/><br/>
          
            <label for='password'>Password: </label>
            <input type="password" name="password" id='password'> <br/><br/>
          
            <label for='age'>Age: </label>
            <input type="number" name="age" id='age' value="1" min="14"> <br/><br/>
          
            <button>Submit</button>
          </form>
          
          <script type="text/javascript">
            const email = document.getElementById('email');
          
            email.addEventListener("input", function (event){
            if (email.validity.typeMismatch){
                email.setCustomValidity('enter correct email');
            }
              else{
                email.setCustomValidity('');
              }
            });
          
          </script>
          
        </body>
      </html>
          
      
    • <!DOCTYPE html>
      <html>
          
      <head>
          <meta charset="utf-8">
          <title>Detailed custom validation</title>
          <style>
            body {
              font: 1em sans-serif;
              width: 200px;
              padding: 0;
              margin : 0 auto;
            }
          
            p * {
              display: block;
            }
          
            input[type=email]{
              -webkit-appearance: none;
              appearance: none;
          
              width: 100%;
              border: 1px solid #333;
              margin: 0;
          
              font-family: inherit;
              font-size: 90%;
          
              box-sizing: border-box;
            }
          
            /* This is our style for the invalid fields */
            input:invalid{
              border-color: #900;
              background-color: #FDD;
            }
          
            input:focus:invalid {
              outline: none;
            }
          
            /* This is the style of our error messages */
            .error {
              width  : 100%;
              padding: 0;
          
              font-size: 80%;
              color: white;
              background-color: #900;
              border-radius: 0 0 5px 5px;
          
              box-sizing: border-box;
            }
          
            .error.active {
              padding: 0.3em;
            }
          </style>
      </head>
          
      <body>
        <form novalidate>
          <p>
            <label for="mail">
              <span>Please enter an email address:</span>
              <input type="email" id="mail" name="mail" required minlength="8">
              <span class="error" aria-live="polite"></span>
            </label>
          </p>
          <button>Submit</button>
        </form>
          
        <script>
          // There are many ways to pick a DOM node; here we get the form itself and the email
          // input box, as well as the span element into which we will place the error message.
          const form  = document.getElementsByTagName('form')[0];
          
          const email = document.getElementById('mail');
          const emailError = document.querySelector('#mail + span.error');
          
          email.addEventListener('input', function (event) {
            // Each time the user types something, we check if the
            // form fields are valid.
          
            if (email.validity.valid) {
              // In case there is an error message visible, if the field
              // is valid, we remove the error message.
              emailError.innerHTML = ''; // Reset the content of the message
              emailError.className = 'error'; // Reset the visual state of the message
            } else {
              // If there is still an error, show the correct error
              showError();
            }
          });
          
          form.addEventListener('submit', function (event) {
            // if the form contains valid data, we let it submit
          
            if(!email.validity.valid) {
              // If it isn't, we display an appropriate error message
              showError();
              // Then we prevent the form from being sent by canceling the event
              event.preventDefault();
            }
          });
          
          function showError() {
            if(email.validity.valueMissing) {
              // If the field is empty
              // display the following error message.
              emailError.textContent = 'You need to enter an e-mail address.';
            } else if(email.validity.typeMismatch) {
              // If the field doesn't contain an email address
              // display the following error message.
              emailError.textContent = 'Entered value needs to be an e-mail address.';
          } else if(email.validity.tooShort) {
              // If the data is too short
            // display the following error message.
              emailError.textContent = `Email should be at least ${ email.minLength } characters; you entered ${ email.value.length }.`;
            }
          
            // Set the styling appropriately
            emailError.className = 'error active';
          }
        </script>
      </body>
          
      </html>
      
    • a