A look at HTML5 Form Validation
I'm doing research for my upcoming HTML5 presentation at cfObjective. One of the areas that I find most interesting is updates to forms. I'm probably biased since I've worked on so many web applications as opposed to front end/marketing type sites, but I tend to think forms are pretty critical. (And no big surprise - my other favorite areas of HTML5 involve data and networking.) I've previously looked at updates to form fields, but I had not really spent a lot of time looking at validation. I spent some time on it this week and have come away pretty damn impressed. Here is the first in a series of blog entries planned on the topic.
In today's blog entry, I want to look at validation at the tag level. Tomorrow's entry (well, the next entry) will look at some of the JavaScript involved, but I was very surprised by how much you could do without writing a line of JavaScript. At a high level, validation in HTML5 comes down to three features:
- Adding a required attribute to a form field will make it required. Duh.
- Adding a type attribute to a form field will make the form check your input and ensure it matches the type.
- And finally - if you match the two (a type and a required attribute) then you will be required to enter something and have it match the type.
What that means is that adding basic validation is as trivial as adding the word "required" to your HTML. Consider this login form:
2 <label for="username">Username</label>
3 <input id="username" name="username" required>
4
5 <label for="password">Password</label>
6 <input id="password" name="password" type="password" required>
7 <p/>
8 <input type="submit" class="btn" value="Login">
9
10</form>
That's it. No JavaScript library. No form submit handler. Nada. In compliant browsers, you immediately get visual feedback when submitting the form:

The browser actually prevents you from submitting the form. Entering a value for the username and then submitting again automatically moves the error to my password field:

How much simpler can it get? Now let's talk about type validation. HTML5 adds a variety of new input types. Two of them are particularly useful - email and url. These form fields render the exact same as any normal text field, but in terms of validation, they will be a bit more picky about what you enter. We just demonstrated a login form, now let's look at a slightly more complex registration form.
2 <label for="username2">Username</label>
3 <input id="username2" name="username" required>
4
5 <label for="password2">Password</label>
6 <input id="password2" name="password" type="password" required>
7
8 <label for="email">Email</label>
9 <input id="email" name="email" type="email" required>
10
11 <label for="url">Homepage</label>
12 <input id="url" name="url" type="url">
13
14 <p/>
15 <input type="submit" class="btn" value="Register">
16
17</form>
Notice I've still got a required username and password. I've added an email field that is required (note the use of type="email") and an optional url field. The type is set to url but it is not required.
Just by changing the types and adding required, I've now added logic to my form such that:
- Username and password will be required.
- Email will be required, and it must be a valid email address.
- Homepage is not required, but if I type anything in, it must be a valid URL. (And by valid we mean syntax wise. It could be a valid URL and not actually resolve to a host.)
Want to play with this? Then check out my demo: http://www.raymondcamden.com/demos/2012/mar/16/forms/10_validation.html
But wait! There's more. What if you want to do a more customized validation? I mentioned earlier that we can use JavaScript, and I plan on talking about that more in the next blog entry. But there is also another option: Regular Expressions. You can add simple regex checks to a field by using the pattern attribute. I found a great example of this that adds simple logic to require a minimum size: pattern="[a-zA-Z0-9]{5,}". Here's a modified form of our login and registration form that will require a username and password to be 5 characters long. Usernames are also set to require letters and numbers only.
2<form action="" autocomplete="off">
3 <label for="username">Username</label>
4 <input id="username" name="username" pattern="[a-zA-Z0-9]{5,}" title="Minimum 5 letters or numbers." required>
5 <!-- Credit: http://stackoverflow.com/questions/5458076/regex-in-new-pattern-attr-for-html5-forms -->
6 <label for="password">Password</label>
7 <input id="password" name="password" type="password" pattern=".{5,}" title="Minmimum 5 letters or numbers." required>
8 <p/>
9 <input type="submit" class="btn" value="Login">
10
11</form>
12
13<h2>Register</h2>
14<form action="" autocomplete="off">
15 <label for="username2">Username</label>
16 <input id="username2" name="username" pattern="[a-zA-Z0-9]{5,}" title="Minmimum 5 letters or numbers." required>
17
18 <label for="password2">Password</label>
19 <input id="password2" name="password" type="password" pattern=".{5,}" title="Minmimum 5 letters or numbers." required>
20
21 <label for="email">Email</label>
22 <input id="email" name="email" type="email" required>
23
24 <label for="url">Homepage</label>
25 <input id="url" name="url" type="url">
26
27 <p/>
28 <input type="submit" class="btn" value="Register">
29
30</form>
Note the use of the title attribute to provide a hint to the user. In Chrome and Firefox, this is even rendered in the error:

You can view a demo of this one here: http://www.raymondcamden.com/demos/2012/mar/16/forms/11_validation_regex.html
So - the big question is - what in the heck happens with browsers that don't support this? Nothing. And there's nothing at all wrong with that. Why? First off - when these new form features "fail", they fail well. The user doesn't see anything weird - they just seem forms. Secondly - we all use server-side validation and don't ever assume client side validation is going to work. No one makes that mistake. No one. So that being said - even with easy to use jQuery-based solutions, I can see skipping that and just relying on this. For folks with decent browsers, they get a great feature with about 2 seconds of my time to code it. For folks with crap browsers (hint - rhymes with "my pee"), they still get a usable form and server-side validation.

huhuhhuhhuhuh.
Chrome however didn't - then I noticed I had my font size jacked up slightly. Resetting my font size it displayed correctly.
FF seems to handle the font size better but not perfect.
We're getting there but it's still frustrating to see such differences in browsers.
We have been a very heavy user of xForms/cfForm. The xForms solution works fantastic 99% of the time and we've customized it a lot, however, when you want to do something 'special' we have to work very hard to make it happen. I have all ready written a replacement set of custom tags that will give us the same experience. Basically wrapping cfinput... but now you are giving me ideas... I digress to my original question....
@Patrick: So yeah, see what Jason found. It's not going to be perfect. :)
"First, if JS would be disabled, some fields would keep being validated while the conditional fields not."
To me, and this is just my opinion, it falls into the 'your browser is so sucky it doesnt matter' category, and since we are using server-side validation as a 100% backend, we are safe.
"Second, even with the use of JS, how could you present/style the error message box in order to be consistent with the default one?"
I think you would get around that by using JS to manipulate the DOM. So imagine an email field that does NOT have the required field. If you click on some checkbox for a newsletter, for example, I'd use JS to add the required attribute to the DOM. Then when you submit the form, the UI should be the exact same as everything else.
That's just an idea though - haven't tested it. (But going to since now I'm curious.)
a. Html5 Form doesn't need a method=["post"|"get"] so, how does it know what is what?
b. Running the demo, after the username/password/email/etc. have been submitted, they appear in the url bar. Should that happen?
b) Yep, because of A. Normally I'd have added method="post".
How would you suggest one can offer HTML5 validation and server-side validation in one application?
Absolutely not! That's one way of doing it but not the only way. I never recommend cfinput myself.
So to answer your last question, I'd just do the validation manually.
I was just trying to provide a little pseudo code to back you up, because I completely agree that hidden fields don't address the server-side solution YOU were suggesting ;)