HTML5

Validating Forms
With HTML5

HTML5

TJ VanToll

Constraint Validation

What is constraint validation?

An algorithm run whenever a user submits a form.

Constraint validation algorithm

Basic Form (No constraints)

<form>
    <input type="text">
    <button>Go</button>
</form>

Making the field required - old school

document.getElementById( "myForm" )
  .addEventListener( "submit", function( event ) {
    if ( document.getElementById( "myInput" ).value == "" ) {
        event.preventDefault();
        // Show some questionably UX and a11y friendly
        // error message.
      }
  });
});

Making the field required - HTML5

<form>
    <input type="text" required>
    <button>Go</button>
</form>
Current browser support of constraint validation http://caniuse.com/#feat=form-validation

Agenda

  • HTML attributes
  • DOM API
  • CSS hooks
  • How to use constraint validation today

Form element attributes

  • min
  • max
  • maxlength
  • pattern
  • required
  • step
  • type

min, max

<form>
  <input type="number" required
    min="2" max="10">
  <button>Go</button>
</form>

maxlength

<form>
  <input type="text" maxlength="2">
  <button>Go</button>
</form>

The spec

Constraint validation: If an element has a maximum allowed value length, its dirty value flag is true, its value was last changed by a user edit (as opposed to a change made by a script), and the code-unit length of the element’s value is greater than the element’s maximum allowed value length, then the element is suffering from being too long.

maxlength

<form>
  <input type="text" maxlength="2"
    value="1234">
  <button>Go</button>
</form>

pattern

<form>
  <input type="text"
    pattern="[0-9].[0-9][0-9]">
  <button>Go</button>
</form>

Provide a title attribute for context

<form>
  <input type="text"
    title="In x.xx format"
    pattern="[0-9].[0-9][0-9]">
  <button>Go</button>
</form>

http://html5pattern.com

pattern

<form>
  <input type="text"
    pattern="(?:19|20)(?:(?:[13579][26]|[02468][048])-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-9])|(?:(?!02)(?:0[1-9]|1[0-2])-(?:30))|(?:(?:0[13578]|1[02])-31))|(?:[0-9]{2}-(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-8])|(?:(?!02)(?:0[1-9]|1[0-2])-(?:29|30))|(?:(?:0[13578]|1[02])-31)))">
  <button>Go</button>
</form>

required

<form>
  <input required>
  <input type="checkbox" required>
  <input type="radio" name="boxes" required>
  <input type="radio" name="boxes" required>
  <select required>
    <option selected></option>
    <option>One</option>
  </select>
  <textarea required></textarea>
  <button>Go</button>
</form>

step

<form>
  <input type="number" step="2">
  <button>Go</button>
</form>

type

<form>
  <input type="email" required>
  <input type="url" required>
  <button>Go</button>
</form>

type="date"

<form>
  <input type="date">
  <button>Go</button>
</form>

Preventing validation

novalidate attribute

Boolean attribute that can be applied to <form> elements to prevent validation.

<form novalidate>
  <input type="text" required>
  <button>Go</button>
</form>

formnovalidate attribute

Boolean attribute that can be applied to submit buttons to prevent validation.

<form>
  <input type="text" required>
  <button>Validate</button>
  <button formnovalidate>
    Do NOT Validate</button>
</form>

Agenda

  • HTML attributes
  • DOM API
  • CSS hooks
  • How to use constraint validation today

Constraint Validation API

Constaint Validation API

  • willValidate
  • validity
  • validationMessage
  • checkValidity()
  • reportValidity()
  • setCustomValidity()

willValidate

Boolean property indicating whether a node is a candidate for constraint validation.

<div id="one"></div>
<input type="text" id="two">
<input type="text" id="three" disabled>
<script>
  document.getElementById( "one" ).willValidate; //undefined
  document.getElementById( "two" ).willValidate; //true
  document.getElementById( "three" ).willValidate; //false
</script>

Use care with hidden fields

<form>
    <input type="text" required>
    <button>Go</button>
</form>

validity

A ValidityState object with the following boolean properties:

  • valueMissing
  • typeMismatch
  • patternMismatch
  • tooLong
  • rangeUnderflow
  • rangeOverflow
  • stepMismatch
  • badInput
  • customError
  • valid

validationMessage

The message the browser displays to the user when a node's validity is checked and fails.

<form>
  <input type="text" id="one" required>
</form>
<script>
  document.getElementById( "one" ).validationMessage;
  // Chrome  --> "Please fill out this field."
  // Firefox --> "Please fill out this field."
  // Safari  --> "value missing"
  // IE      --> "This is a required field."
</script>

checkValidity()

Method that determines the validity of <form> nodes and form elements.

<form id="form">
  <input type="text" id="one" required>
  <input type="text" id="two">
</form>
<script>
  document.getElementById( "one" ).checkValidity(); // false
  document.getElementById( "two" ).checkValidity(); // true
  document.getElementById( "form" ).checkValidity(); // false
</script>

reportValidity()

Same as checkValidity(), but show the bubble.

Not supported in any browsers yet.

setCustomValidity()

Set validationMessage and create custom validation rules.

<form id="form">
  <input type="text" id="input-2">
  <button type="submit">Go</button>
</form>
<script>
  var input = document.getElementById( "input-2" );
  input.setCustomValidity( "Custom error message" );
</script>

Password Fields

<form id="form">
  <input type="password" id="pass-1" required>
  <input type="password" id="pass-2" required>
  <button type="submit">Go</button>
</form>
<script>
  var password1 = document.getElementById( "pass-1" ),
    password2 = document.getElementById( "pass-2" );

  function checkPasswordValidity() {
    if ( password1.value != password2.value ) {
      password1.setCustomValidity( "Passwords must match." );
    } else {
      password1.setCustomValidity( "" );
    }
  }

  password1.addEventListener( "change", checkPasswordValidity );
  password2.addEventListener( "change", checkPasswordValidity );
</script>

invalid event

invalid event

<form id="form">
  <input type="text" id="input-1" required>
</form>
<script>
  document.getElementById( "input-1" )
    .addEventListener( "invalid", function(event) {
      var text = document.createTextNode( "INVALID!" );
      event.target.parentNode.appendChild( text );
    });
  document.getElementById( "input-1" ).checkValidity();
</script>

invalid event

<form>
  <input type="text" id="input-10" required>
  <button>Go</button>
</form>
<script>
  document.getElementById( "input-10" )
    .addEventListener( "invalid", function(event) {
      var text = document.createTextNode( "INVALID!" );
      event.target.parentNode.appendChild( text );
    });
</script>

invalid event

<form>
  <input type="text" id="test-1" required>
  <input type="text" id="test-2" required>
  <button>Go</button>
</form>
<script>
  function background( event ) {
    event.target.style.background = "red";
  }
  document.getElementById( "test-1" )
    .addEventListener( "invalid", background );
  document.getElementById( "test-2" )
    .addEventListener( "invalid", background );
</script>

Showing all error messages

Demo

Showing all error messages

http://tjvantoll.com/2012/08/05/html5-form-validation-showing-all-error-messages/

Spec changes

An invalid event on forms!

Agenda

  • HTML attributes
  • DOM API
  • CSS hooks
  • How to use constraint validation today

:required & :optional

<form id="form-1">
  <input type="text">
  <input type="text" required>
</form>
<style>
  #form-1 :required { background: red; }
  #form-1 :optional { background: blue; }
</style>

:valid & :invalid

<form id="form-2">
  <input type="text" required value="Lorem Ipsum">
  <input type="text" required>
</form>
<style>
  #form-2 :valid { background: green; }
  #form-2 :invalid { background: red; }
</style>

Per the spec, :valid and :invalid apply to form nodes as well. Only implemented in Firefox.

<form id="form-a">
  <input type="text" required value="Lorem Ipsum">
  <input type="text" required>
</form>
<style>
  #form-a:valid { background: green; }
  #form-a:invalid { background: red; }
</style>

Resetting Browser Defaults

:invalid {
    box-shadow: none; /* FF */
    outline: 0;       /* IE */
}

Problem

:valid & :invalid apply immediately, before user interaction.

:moz-ui-valid & :moz-ui-invalid

Only match after interaction or attempted submission (full algorithm).

<form id="form-3">
  <input type="text" required value="Lorem Ipsum">
  <input type="text" required>
  <button type="submit">Go</button>
</form>
<style>
  #form-3 :-moz-ui-valid {
    background: green; color: white;
  }
  #form-3 :-moz-ui-invalid {
    background: red; color: white;
  }
</style>

CSS Selectors Level 4 Spec

Only use :valid and :invalid after interaction

Demo

Bubbles!

Display of required fields in supporting browsers

Styling the Bubbles

You can't.

Old WebKit Hooks

WebKit used to have these pseudo-elements.

  • ::-webkit-validation-bubble
  • ::-webkit-validation-bubble-arrow
  • ::-webkit-validation-bubble-arrow-clipper
  • ::-webkit-validation-bubble-heading
  • ::-webkit-validation-bubble-message
  • ::-webkit-validation-bubble-text-block

You used to be able to do this

Styling the Bubbles

Chrome 28 removed the pseudo-elements for styling form validation messages.

Turning the bubbles off

<form id="form-b">
    <input required>
    <button>Go</button>
</form>
<script>
    document.getElementById( "form-b" )
        .addEventListener( "invalid", function( event ) {
            event.preventDefault();
        }, true );
</script>

Agenda

  • HTML attributes
  • DOM API
  • CSS hooks
  • How to use constraint validation today

Dealing with Unsupported Browsers

  • Browsers
    • IE <= 9
    • Safari
    • iOS Safari
    • Default Android browser
  • Options
    • Fallback to server-side validation
    • Polyfill
    • Use a library based on HTML5

Option #1

Fallback to server-side validation

Why validate server-side?

  • Client side constraints are easily subverted by malicious users.
  • HTTP requests do not have to originate from a browser.

    curl --data "param1=value1" http://foo.com/bar.do
    						
  • So you must do validation on the server.
  • Client-side validation is progressive enhancement.
  • Client-side validation = duplicate effort

Server-side validation

Demo

Option #2

Polyfill

H5F

Demo
https://github.com/ryanseddon/H5F

Webshims

http://afarkas.github.io/webshim/demos/demos/webforms.html

Option #3

Use a library based on HTML5

jQuery validation plugin

Kendo UI Validator

Options

  1. Use server-side validation as a fallback.
  2. Use a polyfill.
  3. Use a library based on HTML5.
    • jQuery validation plugin
    • Kendo UI Validator

<shameless-plug>


http://manning.com/vantoll

</shameless-plug>

Thanks

TJ VanToll / @tjvantoll