HTML5 Form Validation

Why no one uses it, and how you can!

TJ VanToll | @tjvantoll

Forms are hard

Traditional form validation is hard

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

HTML5 form validation is totally cool

<form>
    <input required>
    <button>Submit</button>
</form>
	

Native Form Validation: A quick history

“No one uses HTML5 form validation”

-Me

What I want

Reality

<form>
    <input required type="email">
    <button>Submit</button>
</form>
	

Why does no one use it?

Problems:

  1. Browser support is weird
  2. Customizing error messages is verbose
  3. Aggregating error messages is tricky
  4. The :invalid pseudo-class isn't what you want
  5. You cannot style the bubbles

Problem #1: Browser support is weird

http://caniuse.com/#feat=form-validation

The curious case of Benjamin Button constraint validation on Safari, iOS Safari, and the Android browser.

Safari/iOS Safari/Android Browser let this submit empty

<form>
    <input required>
    <button>Submit</button>
</form>
	

Two scenarios to account for

  • Old IE
  • Safari / iOS Safari / Android Browser

Your server is your fallback

Client-side validation is no substitute for server-side validation

		curl --data "param1=DELETE * FROM *" http://foo.com/bar.do
	

Your server is your fallback

Example

Problem #2: Customizing error messages is verbose

setCustomValidity() is an odd API

Customizing messages

<form>
    <input required type="email" id="baz">
    <button>Go</button>
</form>
<script>
    var baz = document.querySelector( "#baz" );
    function setErrorMessage() {
        if ( baz.validity.valueMissing ) {
            baz.setCustomValidity( "Email is required" );
        } else if ( baz.validity.typeMismatch ) {
            baz.setCustomValidity( "Please provide a valid email address" );
        }
    };
    setErrorMessage();
    baz.addEventListener( "change", setErrorMessage );
</script>

x-moz-errormessage

<form>
    <input required x-moz-errormessage="INVALID!">
    <button>Go</button>
</form>

W3C Bug #10923: Add an attribute to override UI's validation message

title attribute

<form>
    <input pattern="[0-9]{5}" title="Enter 5 numbers">
    <button>Go</button>
</form>

Problem #3: Aggregating error messages is tricky

There is no way to determine when the user attempted to submit a form.

<form id="foo">
    <input required>
    <button>Go</button>
    <script>
        document.querySelector( "#foo" )
            .addEventListener( "submit", function() {
                alert( "hi!" );
            });
    </script>
</form>
	

Aggregating Messages

Demo

Coming soon: invalid event on <form> elements!

Problem #4: The :invalid pseudo-class isn't what you want

:invalid applies immediately!

<form>
    <input type="text" id="foo" required>
</form>
<style>
    #foo:invalid { background: red; }
</style>

:moz-ui-invalid

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

<form>
    <input type="text" id="bar" required>
    <button>Go</button>
</form>
<style>
    #bar:-moz-ui-invalid { background: red; }
</style>

CSS Selectors Level 4 Spec

Only use :invalid after interaction

Demo

Problem #5: You cannot style the bubbles

Bubbles!

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>
    <input required id="field-x">
    <button>Go</button>
</form>
<script>
    document.getElementById( "field-x" )
        .addEventListener( "invalid", function( event ) {
            event.preventDefault();
        });
</script>

Turning the bubbles off (for a whole form)

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

Building your own bubbles

Demo

Problems:

  1. Browser support is weird
  2. Customizing error messages is verbose
  3. Aggregating error messages is tricky
  4. The :invalid pseudo-class isn't what you want
  5. You cannot style the bubbles

Production usage: your options

  1. Server-side fallback only
  2. Polyfill
  3. Use a library based on HTML5

Option #1: Server-side fallback only

Demo

Option #2: Polyfill

Webshims

Demo

Webshims configuration

See http://afarkas.github.io/webshim/demos/

Option #3: Use a library based on HTML5

jQuery validation plugin

Kendo UI Validator

Production usage: your options

  1. Server-side fallback only
  2. Polyfill
  3. Use a library based on HTML5

<shameless-plug>


Use code jqchtwcf for 43% off

http://manning.com/vantoll

</shameless-plug>

Thanks

TJ VanToll / @tjvantoll