HTML5 Form Validation - The Constraint Validation API

This post is more than 2 years old.

In my last blog entry, I introduced the basic concepts of form validation under HTML5. The focus was on basic tag/attribute support. In other words, what could be done without writing JavaScript code. In today's entry I want to talk about how you can use JavaScript to further enhance and work with validation, specifically the Constraint Validation API.

At a high level, this API covers the following features:

  • Form fields have a validity property. This property is a set of keys and boolean values that represent the validity, or lack of, of a particular form. A great example of this is the numeric field type. You can specify that a form field should be numeric, higher than 0, and less than 70. The validity property would actually be able to tell you if the value wasn't a number, or was too low or too high.
  • Form fields also have a generic checkValidity() function that returns true or false. So if you don't care why a field is invalid, you can simply use that. Or you can use that before digging into the validity property to determine exactly why the field isn't valid.
  • Finally, there is a setCustomValidity() method that lets you create a custom validation error. You can think of this like a throw() call as it both lets you set a message and, by default, sets the field as being in an error state. If you use an empty string, the field is considered valid.

I began my investigation by looking at the validity property. If you read the spec (no, I'm not joking, you really should), you'll see a nice list of the validity properties and their meanings. I'm quoting here from that spec:

element . validity . valueMissing
Returns true if the element has no value but is a required field; false otherwise.

element . validity . typeMismatch
Returns true if the element's value is not in the correct syntax; false otherwise.

element . validity . patternMismatch
Returns true if the element's value doesn't match the provided pattern; false otherwise.

element . validity . tooLong
Returns true if the element's value is longer than the provided maximum length; false otherwise.

element . validity . rangeUnderflow
Returns true if the element's value is lower than the provided minimum; false otherwise.

element . validity . rangeOverflow
Returns true if the element's value is higher than the provided maximum; false otherwise.

element . validity . stepMismatch
Returns true if the element's value doesn't fit the rules given by the step attribute; false otherwise.

element . validity . customError
Returns true if the element has a custom error; false otherwise.

element . validity . valid
Returns true if the element's value has no validity problems; false otherwise.

That's quite a few properties. I created a form that made use of various new input types and wrote code to iterate over those properties and write them out so I could see in real time how the values would change based on the inputs I provided.

var fields = document.querySelectorAll("input"); for(var i=0,len=fields.length; i<len; i++) { var thisId = fields[i].id; var s = "<div class='results'>Validity for "+thisId; s += "<table>"; //first, call checkValidity as a whole s += "<tr><td><b>VALID:</b></td><td>"+fields[i].checkValidity()+"</td></tr>"; for(prop in fields[i].validity) { s += "<tr><td>"+prop+"</td><td>"+fields[i].validity[prop]+"</td></tr>"; } s+= "</table></div>"; fields[i].insertAdjacentHTML("afterend",s); }

You can see my simple for/in loop there generating nice table rows. Also note I use the checkValidity API to show, at a high level, if the field is valid or not.

Here's an example:

I mentioned the checkValidity API and how it can be used as a shortcut for determining if a field is valid. What's cool is that you can also do it for the entire form. Here is another example:

//check the form as a whole var form = getBySel("#mainForm"); var formStatus = getBySel("#formStatus"); formStatus.innerHTML = "<p>Form validity as a whole is "+form.checkValidity()+"</p>";

As a quick aside, getBySel was just a shortcut function I wrote:

//jQuery is a drug - a shiny, happy drug... function getBySel(id) { return document.querySelector(id); }

Put together, I've got a nice demo application that lets you test this API against a variety of constraints. Since it is entirely client-side, I'll encourage you to View Source:

Now let's look at the next piece of the puzzle - custom validation. I mentioned the setCustomValidity API before. It works like so:

Given a field - if you set any non-empty string value via setCustomValidity, it is implied that the field is in error. If you set an empty string, it is implied that the field is ok.

So that makes sense, but it feels a bit awkward to me. I know I tend to rail against making things overly complex, but it feels weird that setting a string value for the message also sets the field as being invalid. I'd rather a two step process along the lines of setValid(false);setCustomValidity("....");.

Let's look at an example. This is a bit contrived, but in the form below we want to error out if the value is ray.

<!DOCTYPE html> <html> <head> <script> function getBySel(id) { return document.querySelector(id); }

function demoForm() { var field = getBySel("#field1"); var value = field.value; if(value == 'ray') { field.setCustomValidity("Ray is wack!"); } else { field.setCustomValidity(""); } }

function init() { getBySel("#testForm").addEventListener("click",demoForm,false); } </script> <style> </style> </head> <body onload="init()">

<form>

&lt;p&gt;
    Just required:
    &lt;input type="text" id="field1"&gt;
&lt;/p&gt;

&lt;p&gt;
    &lt;button id="testForm"&gt;Test&lt;/button&gt;
&lt;/p&gt;

</form> </body> </html>

As I said - it's a rather trivial example, but you can see where I use a simple event handler to check the value of a form field. It is important to remember that once you setCustomValidity and pass a non-empty string, the field is considered in error until you setCustomValidity with an empty string. Basically you've marked the field as bad until you explicitly set it to good.

Obviously one could do something a bit more complex here. You're options are pretty much anything at all. You could do an Ajax request to ensure the value was unique. You could check other values in the form. Try the demo yourself:

Raymond Camden's Picture

About Raymond Camden

Raymond is a senior developer evangelist for Adobe. He focuses on document services, JavaScript, and enterprise cat demos. If you like this article, please consider visiting my Amazon Wishlist or donating via PayPal to show your support. You can even buy me a coffee!

Lafayette, LA https://www.raymondcamden.com

Archived Comments

Comment 1 by Tim Garver posted on 3/19/2012 at 7:51 PM

Nice demo Ray.
I love these new features in HTML5.
I was wondering if there is a call back for when the field becomes valid? say its invalid the user had submitted the form, and now they have corrected the error. Does the API have a hook to clear the error messages? or is that something we would have to write as an additional event handler?

Thanks and yet another great post.

Tim

Comment 2 by Raymond Camden posted on 3/19/2012 at 8:04 PM

Awesome question! I had assumed no because I had not found any examples previously, but I looked again, and yes, you can add an event handler for invalid:

getBySel("#field1").addEventListener("invalid",invalidHandler,false);

However - this does not run IMMEDIATELY - but only when a check is made. So by default, when you submit the form.

This is great - and I'll expand this into another blog entry. (It really isn't that complex and this comment explains it, but I'd like to write it up and make a quick demo.)

Comment 3 by Mike posted on 3/20/2012 at 7:32 PM

Couldn't you bind to the onBlur or onFocus to have it fire when they click out of the area so they would not have to hit submit to validate?

Comment 4 by Raymond Camden posted on 3/21/2012 at 12:05 AM

Sure thing.

Comment 5 by A2D posted on 7/15/2013 at 8:46 PM

Came across this post while trying to figure out how to use setCustomValidity() with the webshims forms polyfill. While it is a great library, the documentation is not so clear...
Anyway, turns out setCustomValidity() will NOT work with webshims if you already set a custom validation message on a field using data-errormessage="custom message".
That took ages to figure out - thanks for making this a lot clearer.

Comment 6 by Raymond Camden posted on 7/15/2013 at 10:56 PM

No problem.

Comment 7 by Dave posted on 11/5/2013 at 11:12 PM

WRT the question: Couldn't you bind to the onBlur or onFocus to have it fire when they click out of the area so they would not have to hit submit to validate?

Could you please help a newbie and provide an example of how to do this?

To get an idea I'm looking at the following example code on http://afarkas.github.io/we... :
/*bind to the first invalid element in a form*/
$('section.forms-demo').bind('firstinvalid.example1', function(e){
/*show the invalid alert for first invalid element*/
$.webshims.validityAlert.showFor( e.target );
/*prevent browser from showing native validation message */
return false;
});

firstinvalid is an event defined in webforms, so I'm thinking that would be (syntactically) equivalant to the onBlur event. But what the heck is "example1" ?? Am I on the right track here?

Comment 8 by Raymond Camden posted on 11/6/2013 at 3:44 AM

Sure - just change my event handler.

Comment 9 by ???? posted on 12/18/2013 at 9:20 PM

it's nice code & usefull solution for me. tnx a lot.
but 1 question :
i use :invalid as pseudo selector instead input in ur code.
in my case i have 2 invalid input on form, but when i alert len, 3 is shown. why?

Comment 10 by Raymond Camden posted on 12/18/2013 at 9:23 PM

Can you share a link to the HTML so we can see?

Comment 11 by Amin posted on 12/19/2013 at 12:14 AM

yes. of course. sorry for the late reply. my code is on my localhost. & i try to simplify it :

http://jsfiddle.net/AminPou...

Comment 12 by Raymond Camden posted on 12/19/2013 at 12:31 AM

I've tried your fiddle but i can't get it to alert. I hit the Edit link and it just tries to submit.

Comment 13 by Amin posted on 12/19/2013 at 4:36 PM

hi. you are right. i update my fiddle :
http://jsfiddle.net/AminPou...

please recheck it. invalid input colored with "yellow". move your mouse on "edit" and see what happen. thanks again.

Comment 14 by Raymond Camden posted on 12/19/2013 at 5:15 PM

In Chrome the alert showed 2, not 3. What browser did you test with?

Comment 15 by Amin posted on 12/19/2013 at 5:40 PM

Firefox 25.0.1

Comment 16 by Raymond Camden posted on 12/19/2013 at 6:02 PM

Oh fascinating. In FF, the form element is being marked as invalid in FF but not Chrome. In other words, FF considers the form tag invalid because one of the kids is invalid. Fascinating.

I'll dig a bit and say more a bit later today.

Comment 17 by Raymond Camden posted on 12/19/2013 at 6:14 PM

Here is a simpler example: http://jsfiddle.net/RxLzM/.

Comment 18 by Amin posted on 12/19/2013 at 6:28 PM

ok. helpfull note. thus the exact count of invalid input can get by max(document.querySelector(':invalid') - 1 , 0); in FF.
as the FF count them 0,2,3,4,...

Comment 19 by Raymond Camden posted on 12/19/2013 at 6:49 PM

See my latest blog post. I provide a simpler solution.

Comment 20 by Amin posted on 12/19/2013 at 7:07 PM

perfect. u skip the form although it has invalid input. tnx.
can i ask u more question?

Comment 21 by Raymond Camden posted on 12/19/2013 at 7:59 PM

Technically I skip the Form tag element. Not the entire form. :)

Yes you can ask another question, but if it isn't related to this blog entry I ask that you use my contact form instead.

Comment 22 by Amin posted on 12/20/2013 at 1:16 PM

yes. it is technically that i did'nt know before. about "more question" i found my answer by myself. tnx.
"check validation of entire form instead of check input element one by one with test() or match()", it's my problem that i thought it's possible but i didn't know how? i appreciate that i found answer of my question in your blog. tnx again & good luck Mr. Ray.

pardon 4 my poor english.

Comment 23 by Amin posted on 1/2/2014 at 12:31 PM

hi mr. camden. happy new year.
i have a question if this blog is where i can ask it. what is the difference between reg expression in js & html5 pattern attribute in style or syntax? if there is no (as i think), what's wrong in my code for validating 12'hours input pattern based on :

http://www.mkyong.com/regul...

my code is : <input type="text" pattern="(1[012]|[1-9]):[0-5][0-9](\\s)?(?i)(am|pm)"/>

Comment 24 by Raymond Camden posted on 1/2/2014 at 5:09 PM

Can you specify how it is failing you?

Comment 25 by Amin posted on 1/2/2014 at 11:59 PM

any input consider valid :
http://jsfiddle.net/AminPou...

Comment 26 by Raymond Camden posted on 1/3/2014 at 12:13 AM

Confirmed. Have you tested the regex in JS to be *sure* it works?

Comment 27 by Raymond Camden posted on 1/3/2014 at 12:14 AM

I just tested the regex in JS and it returns an invalid group.

Comment 28 by Amin posted on 1/3/2014 at 12:32 AM

no. i didnt' test it. based on mentioned in the site that i point it above, i thought it's work. but when i delete `(\\s)?(?i)` from pattern it's work fine, only without Space between hh:mm and am|pm and as u can see upper case of am|pm is not valid.

Comment 29 by Raymond Camden posted on 1/3/2014 at 12:36 AM

Well there ya go. ;) Next time I'd test. This was a good thread though - now we know that the pattern regex for input tags won't throw any type of error you can check. I may follow up with a blog post on this.